一、GIL:全局解释器锁
GIL本质是就是一把互斥锁,是夹在解释器身上的,
同一个进程内的所有线程都需要抢到GIL锁,才能执行解释器代码。
优点:保证cpython 解释器内存管理的线程安全
缺点:同一进程内所有的线程同一时刻只能有一个执行,也就是说cpython解释器的多线程无法实现并行
1、计算密集型应该使用多进程
# from multiprocessing import Process
# from threading import Thread
#
# import time
# # import os
# # print(os.cpu_count())
#
# def task1():
# res=0
# for i in range(1,100000000):
# res+=i
#
# def task2():
# res=0
# for i in range(1,100000000):
# res+=i
#
# def task3():
# res=0
# for i in range(1,100000000):
# res+=i
#
# def task4():
# res=0
# for i in range(1,100000000):
# res+=i
#
# if __name__ == '__main__':
# # p1=Process(target=task1)
# # p2=Process(target=task2)
# # p3=Process(target=task3)
# # p4=Process(target=task4)
#
# p1=Thread(target=task1)
# p2=Thread(target=task2)
# p3=Thread(target=task3)
# p4=Thread(target=task4)
# start_time=time.time()
# p1.start()
# p2.start()
# p3.start()
# p4.start()
# p1.join()
# p2.join()
# p3.join()
# p4.join()
# stop_time=time.time()
# print(stop_time - start_time)
2、IO密集型应该使用多线程
from multiprocessing import Process
from threading import Thread
import time
def task1():
time.sleep(3)
def task2():
time.sleep(3)
def task3():
time.sleep(3)
def task4():
time.sleep(3)
if __name__ == '__main__':
# p1=Process(target=task1)
# p2=Process(target=task2)
# p3=Process(target=task3)
# p4=Process(target=task4)
# p1=Thread(target=task1)
# p2=Thread(target=task2)
# p3=Thread(target=task3)
# p4=Thread(target=task4)
# start_time=time.time()
# p1.start()
# p2.start()
# p3.start()
# p4.start()
# p1.join()
# p2.join()
# p3.join()
# p4.join()
# stop_time=time.time()
# print(stop_time - start_time) #3.138049364089966
p_l=[]
start_time=time.time()
for i in range(500):
p=Thread(target=task1)
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(time.time() - start_time)
三、线程互斥锁与GIL对比
GIL只保护解释级别的数据,不能保护线程里的数据
from threading import Thread,Lock
import time
mutex=Lock()
count=0
def task():
global count
mutex.acquire()
temp=count
time.sleep(0.1)
count=temp+1
mutex.release()
if __name__ == '__main__':
t_l=[]
for i in range(2):
t=Thread(target=task)
t_l.append(t)
t.start()
for t in t_l:
t.join()
print('主',count)
进程池VS线程池
池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务
池子内什么时候装进程:并发的任务属于计算密集型
池子内什么时候装线程:并发的任务属于IO密集型
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random
def task(X):
print('%s 接收'%os.getpid())
time.sleep(random.randint(2,5))
return X**2
if __name__=='__main__':
p=ProcessPoolExecutor()#默认开启的进程数是cpu的核数
for i in range(20):
p.submit(task,i)
if__name__=='__main__':
p=ThreadPoolEx(4)#默认开启的线程数是cpu的核数*5
for i in range(20):
p.submit(task,i)
四、阻塞与非阻塞指的是程序的两种运行状态
阻塞:遇到IO发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源
非阻塞(就绪或运行状态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地,执行其他操作,力求尽可能多的占有CPU
同步与异步指的是提交任务的两种方式:
同步调用:提交完任务,就在原地等,直到任务执行完毕后,拿到返回值再执行下一行代码
异步调用:提交完任务,不在原地等,直接执行下一行代码。
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,random
def task(X):
print('%s'%X)
time.sleep(random.randint(1,3))
return X**2
if __name__=='__main__'
#异步调用
p=ThreadPoolExecutor(4)#默认开启的线程数是cpu的核数*5
obj_l=[]
for i in range(10):
obj=p.submit(task,i)
obj_l.append(obj)
p.shutdown(wait=True)
print(obj_l[2].result())
print('主')
五基于套接字并发的通信
from socket import *
from threading import Thread
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
tpool=ThreadPoolExecutor(3)
def communicate(conn,client_addr):
while True: # 通讯循环
try:
data = conn.recv(1024)
if not data: break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
def server():
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True: # 链接循环
conn,client_addr=server.accept()
print(client_addr)
# t=Thread(target=communicate,args=(conn,client_addr))
# t.start()
tpool.submit(communicate,conn,client_addr)
server.close()
if __name__ == '__main__':
server()
客户端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()