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