1.GIL 是一个全局解释器锁 ,是一种互斥锁
为什么需要GIL:因为一个python.exe进程中只有一分解释器,如果这个进程开启了多个线程都要执行代码 多线程之间要竞争解释器,一旦竞争就有可能出现问题
带来的问题:同一时间只有一个线程可以访问解释器 好处:保证了多线程的数据完全
thread-safe 线程安全的 多个线程同时访问也不会出问题 not thread-safe 非线程安全的 多个线程同时访问可能会出问题 (加锁)
默认情况下一个进程只有一个线程 是不会出现问题的 ,但是不要忘记还有GC线程 一旦出现多个线程就可能出问题 ,所以当初就简单粗暴的加上了GIL锁
那么多线程是不是完全没有意义? 由于有GIL的存在 即使有多个CPU 也不能真正的并行
有三个任务 三个任务要并发执行 使效率最高 1.多进程 2.同一个进程下多线程
只有一个CPU 如果3个任务都要等待IO 如果是采用方案1: 由于IO的时间较长 不仅不能提高效率 反而无谓的增加了系统开销 方案2 更好
有三个CPU 如果是采用方案1 并且三个任务都没有IO操作: 开启三个进程 并行的来执行 效率更高 如果是采用方案2 并且三个任务都没有IO操作: 开三个线程 必须串行执行 所以效率比进程更低
应用程序分为两种 1.IO密集型 IO操作较多 纯计算较少 采用多线程 2.计算密集型 计算操作较多 IO较少 采用多进程
IO密集型任务:
from multiprocessing import Process from threading import Thread,enumerate,current_thread
import time def task(): with open("xxx","rt",encoding="utf-8") as f: f.read()
if __name__ == '__main__': start = time.time()
for i in range(100): Thread(target=task).start()
# enumerate是所有的线程 for t in enumerate(): if t != current_thread(): t.join()
# ps = [] # for i in range(100): # p = Process(target=task) # p.start() # ps.append(p) # # for p in ps: # p.join()
print(time.time()-start)
计算密集型任务:
from multiprocessing import Process from threading import Thread,enumerate,current_thread
import time def task(): a = 1 for i in range(10000000): a += 1
if __name__ == '__main__': start = time.time()
# 采用多线程 # for i in range(10): # Thread(target=task).start() # # # enumerate是所有的线程 # for t in enumerate(): # if t != current_thread(): # t.join()
ps = [] for i in range(10): p = Process(target=task) p.start() ps.append(p)
for p in ps: p.join()
print(time.time()-start)
应用场景: TCP程序 应该采用多线程 纯计算 例如人脸识别 语音识别等 采取多进程
GIL 是什么 为什么出现 如何解决其带来的效率问题
既然已经有锁了 还需要自己加锁吗? 什么情况需要自己加锁 当多个线程需要共享一个不属于解释器的资源时 必须要自己加 不用加锁的例子: 多个线程要并发修改某一个变量数据
2.线程池 进程池 池就是容器 服务器不可能无线的开线程 ,所以需要对线程数量加以控制,线程池就是帮我们封装了线程数量的控制 以及 线程的创建 销毁 任务的分配 使用方法一样的
特点: 线程池 在创建时不会立即开启线程 等到提交任务时 如果没有空闲线程 并且已存在的线程数量 小于最大值 开个新的
线程开启以后就不会在关闭了 直到进程全部结束为止
from concurrent.futures import ThreadPoolExecutor import threading
def task(): print("running............")
pool = ThreadPoolExecutor(3) pool.submit(task) pool.submit(task) pool.submit(task) pool.submit(task)
print(threading.active_count()) print(threading.enumerate()) import time
time.sleep(3) print(threading.active_count()) print(threading.enumerate())
3.同步 异步 阻塞 非阻塞 阻塞: 程序运行过程中遇到IO操作 无法继续 非阻塞: 程序正在运行中,并且没有遇到IO操作,即时遇到IO也不会阻塞,CPU不会切走
指的是程序的执行的状态
指的是发起任务的方式 同步: 在发起任务后必须在原地等待 任务执行完毕 才能继续往下执行
异步: 在发起任务后立即继续往下执行,不需要等待任务的执行结果 结果上哪去获取???
异步效率高于同步 发起异步任务的方式 就是线程和进程
同步和阻塞 是完全不同的: 阻塞一定是CPU已经切走了 同步虽然也会卡住 但是CPU没切走 还在你的进程中
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor()
import time
def task(num): time.sleep(0.5) print("run.....") return num ** 2
ress = []
for i in range(10): res = pool.submit(task,i) # res.result() 该函数是阻塞 会一直等到任务执行完毕 导致程序串行执行 ress.append(res)
# 保证 当我要获取的时候 所有任务都已经执行完毕 pool.shutdown(wait=True) # 该函数也是阻塞函数
# 等到全部完成在获取结果 for i in ress: print(i.result())
print("over")
|
|