一、线程的概念
首先我们学习了进程,进程有很多优点,它提供了多道编程,提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:
- 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
- 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。
现在我们发现了进程的缺陷了,而解决的办法就是引入了线程的机制。
线程的特点
进程是资源分配的最小单位,线程是CPU调度的最小单位。每一个进程中至少有一个线程。
线程可以利用进程所拥有的资源执行调度和运算。
同一个进程中的线程共享同一内存空间,但是进程之间是独立的。
线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)
全局解释器(GIL)锁
全局解释器锁是CPython解释器的一种机制,只有拿到GIL锁的线程才可以执行,所以代表着,同一时刻,在一个进程中,开启多个线程,也只能有一条线程在执行
二、线程的使用
1、如何开启线程
启动线程和启动进程差不多,只是导入的模块不同
# 方式一
from threading import Thread
def task():
print('我是子线程')
if __name__ == '__main__':
t = Thread(target=task)
t.start()
用类的方式也和进程一样,需要重写run方法
# 方式二
from threading import Thread
class MyThread(Thread):
def run(self) -> None:
print('我是子线程')
if __name__ == '__main__':
t = MyThread()
t.start()
2、Thread类的方法
2-1、Thread实例对象的方法
t.is_alive() # 判断线程是否存活
t.getName() # 查看线程的名字,需要打印出来
t.setName() # 设置线程的名字
t.setDaemon(True) # 守护线程,与守护进程一样,在start前使用,主进程执行结束,子进程也会结束
2-2、threading模块提供的一些方法
threading.currentThread() # 返回当前的线程变量。
threading.enumerate() # 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount() # 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
3、线程和进程的比较
开线程的资源消耗的资源以及消耗的时间 远远小于进程
from threading import Thread
import time
def task():
time.sleep(0.1)
print('我是子线程')
if __name__ == '__main__':
start = time.time()
t = Thread(target=task)
t.start()
t.join()
print('我是主线程')
print('运行时间为', time.time() - start)
'''
上面这段代码使用线程执行,运行时间为0.10秒
但如果用进程执行,运行时间为0.18到0.17秒不等
对于计算机来说这0.08秒的时间 已经算很久了
'''
三、线程队列Queue
线程的Queue与进程的Queue一样,是用于实现线程的通信
from threading import Thread
from queue import Queue
def task(q):
q.put('熊大') # 存值
q.put('熊二')
if __name__ == '__main__':
q = Queue() # Queue实例化
t = Thread(target=task, args=(q,))
t.start()
print(q.get()) # 取值
print(q.get())
'''运行结果
熊大
熊二
'''
线程Queue的属性方法与进程基本一致,这里就不多演示了,
另外还有LifoQueue:先进先出;PriorityQueue:优先级队列,均是queue下的类