day33 GIL锁 线程队列 线程池
1. 全局解释器锁GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
2. 线程队列
queue队列 :使用import queue,用法与进程Queue一样
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
- class
queue.
Queue
(maxsize=0) #先进先出
import queue #先进先出型 q=queue.Queue(3) q.put(3) q.put(2) q.put(1) print(q.get()) print(q.get()) print(q.get()) """ 结果 3 2 1 """
class queue.
LifoQueue
(maxsize=0) #last in fisrt out
import queue q=queue.LifoQueue() q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) """ 结果 3 2 1 """
class queue.
PriorityQueue
(maxsize=0) #存储数据时可设置优先级的队列
import queue q=queue.PriorityQueue() #put进入一个元组,元组的第一个元素一般是优先级(一般是数字,也可以是非数字),数字越小,优先级越小 q.put((-1,"a")) q.put((0,"b")) q.put((10,"c")) q.put((20,"d")) print(q.get()) print(q.get()) print(q.get()) print(q.get()) """ 结果 数字越小,优先级越高,越优先输出 (-1, 'a') (0, 'b') (10, 'c') (20, 'd') """
或者
import queue class A: def __init__(self, property, value): self.property = property self.value = value def __lt__(self, other): return self.property < other.property a = A(1, 'a') b = A(2, 'b') c = A(3, 'aaaaa') d = A(1, 'bbbb') q = queue.PriorityQueue() q.put(a) q.put(b) q.put(c) q.put(d) print(q.get().value) print(q.get().value) print(q.get().value) print(q.get().value) ############# a bbbb b aaaaa
注:优先级相同的会根据第二元素比较,第二个元素是根据编码的顺序比较,并且第二个元素的类型必须一致并且,是有序的才可以比较
如果两个值的优先级一样,那么按照后面的值的acsii码顺序来排序,如果字符串第一个数元素相同,比较第二个元素的acsii码顺序
TypeError: unorderable types: dict() < dict() 不能是字典
优先级相同的两个数据,他们后面的值必须是相同的数据类型才能比较,可以是元祖,也是通过元素的ascii码顺序来排序
3. Python标准模块--concurrent.futures
#1 介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor: 进程池,提供异步调用 Both implement the same interface, which is defined by the abstract Executor class. #2 基本方法 #submit(fn, *args, **kwargs) 异步提交任务 #map(func, *iterables, timeout=None, chunksize=1) 取代for循环submit的操作 #shutdown(wait=True) 相当于进程池的pool.close()+pool.join()操作 wait=True,等待池内所有任务执行完毕回收完资源后才继续 wait=False,立即返回,并不会等待池内的任务执行完毕 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 submit和map必须在shutdown之前 #result(timeout=None) 取得结果 #add_done_callback(fn) 回调函数
import time,random from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor def func(n): time.sleep(random.randint(1,3)) return n*n if __name__ == '__main__': p=ProcessPoolExecutor(max_workers=4) list=[] for i in range(10): ret=p.submit(func,i) list.append(ret) print(ret.result()) p.shutdown() for ret in list: print(ret.result())
from concurrent.futures import ThreadPoolExecutor def func(n): return n**2 if __name__ == '__main__': t_p=ThreadPoolExecutor(max_workers=4) list=[] for i in range(10): ret=t_p.submit(func,i)#异步提交任务 list.append(ret) # print(ret.result()) #等待任务提交之后拿去结果,拿不到就柱塞,拿到了就继续 t_p.shutdown() print("主进程结束") for ret in list: print(ret.result())
from concurrent.futures import ThreadPoolExecutor def func(n): return n*n if __name__ == '__main__': t_p=ThreadPoolExecutor(max_workers=4) # for i in range(10): # t_p.submit(func,i) t_p.map(func,range(10)) #map取代了for+submit
from concurrent.futures import ThreadPoolExecutor def get(n): return n*n def call(m): print(">>",m) print(m.result()) if __name__ == '__main__': t_p=ThreadPoolExecutor(max_workers=4) for i in range(3): t_p.submit(get,i).add_done_callback(call) """ 结果 >> <Future at 0x294c1d0 state=finished returned int> #必须.result才能拿到结果 0 >> <Future at 0x294c1d0 state=finished returned int> 1 >> <Future at 0x294c1d0 state=finished returned int> 4 """