多线程
-
线程的创建和运行
import threading def task(num): pass t = threading.Thread(target=task,args=(num,)) t.start()
-
守护线程
一般情况下,主线程代码执行完毕后不会结束,会等待所有子线程任务结束后才结束
如果将子线程设置为守护线程,意味着:主线程代码执行完毕,主线程和子线程一起立即结束
t.setDaemon(True)
-
多线程的队列
from queue import Queue q = Queue() q.put() # 数据放入队列,队列计数+1 q.get() # 从队列中取出数据,队列计数不变 q.task_done() # 和q.get()配合使用,队列计数-1 q.join() #阻塞主线程,等待队列计数为0时,结束阻塞
多进程
-
进程的创建和运行
import multiprocessing def task(num): pass p1 = multiprocessing.Process(target=task) p1.start()
-
守护进程
将子进程设置为守护进程后,主进程代码执行完毕,主进程和子进程一块结束
p1.deamon=True
-
多进程的队列
from multiprocessing import JoinableQueue as Queue # 不能使用普通队列,因为进程之间是资源独立的,需要使用能够让多进程间共享数据的队列
线程池
使用线程池的好处:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,
使用线程池可以进行统一的分配,调优和监控
线程池的用法
from multiprocessing.dummy import Pool
def task():
pass
pool = Pool(5) # 线程池中子线程的最大数量
pool.apply_async(func=task,args=(,)) # 发布任务,交给线程池的子线程去执行,只是发布
pool.close() # 关闭线程池,不再接收新的任务,不是销毁线程池
pool.join() # 阻塞主线程,等待线程池中所有子线程的任务结束
线程池的回调:
当不清楚,线程池需要做的任务数量时,可以使用线程池的回调
def _callback(temp):
print("之前的线程任务执行完毕")
# 可以在此为线程池添加新的任务去执行
pool.apply_async(func=task,callback=_callback) # 发布任务,交给线程池的子线程去执行,只是发布,当子线程执行完毕时,会自动调用callback
协程池
import gevent.monkey
gevent.monkey.patch_all()
# 协程池
from gevent.pool import Pool
from queue import Queue
# 协程池pool没有close方法
一般情况下,协程池的效率高于线程池
GIL
全局解释器锁(GIL),cpyton解释器中的,限制了多线程,导致同一时刻只有一个线程被调用,而且不能cpu多核的特性
GIL的步骤:
-
切换到一个线程
-
设置GIL锁
-
执行指定字节数量的代码
- 代码执行完毕
- 遇到延迟操作比如(time.sleep(), socket.accept(), socket.connect())
-
释放GIL锁
-
切换到别的线程去执行
由于GIL的限制,对于IO密集型的操作,可以优先使用多线程、协程技术,对于CPU(计算)密集型的操作,使用多进程
并发和并行
有多核心,每个核心执行一个任务,任务数量 <= 核心数,此时时并行
任务数量 >=核心数 ,此时是并发