验证GIL的存在
from threading import Thread
money = 10
def task():
global money
tmp = money
money = tmp - 1
t_list = []
for i in range(10):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
# GIL锁会让线程依次执行
print(money)
# 0
GIL和互斥锁
from threading import Thread
import time
money = 10
def task():
global money
tmp = money
time.sleep(0.1) # 如果线程进入IO操作就会释放GIL锁
money = tmp - 1
t_list = []
for i in range(10):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
# 9
'GIL不会影响程序层面的数据也不会保证数据安全,需要自己加锁'
from threading import Thread, Lock
import time
money = 10
mutex = Lock()
def task():
mutex.acquire() # 抢锁
global money
tmp = money
time.sleep(0.1) # 如果线程进入IO操作就会释放GIL锁
money = tmp - 1
mutex.release() # 释放锁
t_list = []
for i in range(10):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(money)
# 0
验证python多线程是否有用
1.单个CPU
1.1IO密集型
多进程:要申请额外的空间,消耗更多资源
多线程:不需要申请额外空间,通过多道技术
1.2计算密集型
多进程:要申请额外的空间,消耗更多资源,运行时间更长(总耗时+申请空间+拷贝代码+切换)
多线程:不需要申请额外空间,通过多道技术,运行时间更短(总耗时+切换)
2.多个CPU
2.1IO密集型
多进程:总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
多线程:总耗时(单个进程的耗时+IO)术
2.2计算密集型
多进程:总耗时(单个进程的耗时)
多线程:总耗时(多个进程的综合)
'只有多个CPU的计算密集型多进程才更有优势'
3.多个CPU情况下
3.1计算密集型
from threading import Thread
from multiprocessing import Process
import os
import time
def work():
res = 1
for i in range(1, 100000):
res *= i
if __name__ == '__main__':
start_time = time.time()
p_list = []
for i in range(10):
p = Process(target=work)
p.start()
p_list.append(p)
for p in p_list:
p.join()
print(time.time() - start_time)
# 6.014247179031372
t_list = []
for i in range(10):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(time.time() - start_time)
# 23.872171878814697
'''
计算密集型
多进程:6.014247179031372
多线程:23.872171878814697
'''
def work():
time.sleep(1) # 模拟纯IO操作
if __name__ == '__main__':
start_time = time.time()
p_list = []
for i in range(10):
p = Process(target=work)
p.start()
p_list.append(p)
for p in p_list:
p.join()
print(time.time() - start_time)
# 1.3191983699798584
t_list = []
for i in range(10):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(time.time() - start_time)
# 1.0059502124786377
'''
IO密集型
多进程:1.3191983699798584
多线程:1.0059502124786377
'''
死锁现象
'''
虽然我们已经掌握了互斥锁的使用,但在实际项目中尽量少用,可能会出现锁死现象
'''
from threading import Thread, Lock
import time
mutexA = Lock() # 类名加括号每执行一次就会产生一个新的对象
mutexB = Lock() # 类名加括号每执行一次就会产生一个新的对象
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexB.acquire()
print(f'{self.name}抢到了B锁')
mutexB.release()
print(f'{self.name}释放了B锁')
mutexA.release()
print(f'{self.name}释放了A锁')
def func2(self):
mutexB.acquire()
print(f'{self.name}抢到了B锁')
time.sleep(1)
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexA.release()
print(f'{self.name}释放了A锁')
mutexB.release()
print(f'{self.name}释放了B锁')
for i in range(5):
t = MyThread()
t.start()
# Thread-1抢到了A锁
# Thread-1抢到了B锁
# Thread-1释放了B锁
# Thread-1释放了A锁
# Thread-1抢到了B锁
# Thread-2抢到了A锁
信号量
'''
1.信号量本质也是互斥锁,只不过它是多把锁
2.信号量在不同的知识体系中,意思可能有区别,在并发编程中,信号量就是多把互斥锁,在django中,信号量指的是达到某个条件自动触发(中间件)
'''
from threading import Thread, Semaphore
import time
import random
sp = Semaphore(3) # 产生三把锁
class MyThread(Thread):
def run(self):
sp.acquire()
print(self.name)
time.sleep(random.randint(1, 3))
sp.release()
for i in range(7):
t = MyThread()
t.start()
# Thread-1
# Thread-2
# Thread-3
# Thread-5Thread-4
#
# Thread-6
# Thread-7
#
# Process finished with exit code 0
event事件
'子进程\子线程之间可以彼此等待彼此'
from threading import Thread, Event
import time
event = Event()
def light():
print('红灯停')
time.sleep(2)
print('绿灯行')
event.set()
def car(name):
print('等红灯')
event.wait()
print(f'绿灯 {name}冲啊')
t = Thread(target=light)
t.start()
for i in range(5):
t = Thread(target=car, args=(f'飙车党{i}',))
t.start()
# 红灯停
# 等红灯
# 等红灯
# 等红灯
# 等红灯
# 等红灯
# 绿灯行
# 绿灯 飙车党0冲啊绿灯 飙车党1冲啊
# 绿灯 飙车党3冲啊
# 绿灯 飙车党4冲啊
#
# 绿灯 飙车党2冲啊
进程池和线程池
'''
1.在实际应用中是不是可以无限制的开进程和线程,如果开的进程和线程过多,会造成内存溢出
2.进程池计算提前创建好固定个数的进程供程序使用,后续不会再创建
3.线程池就是提前创建好固定个数的线程供程序使用,后续不会再创建
'''
from concurrent.futures import ProcessPoolExecutor
import os
import time
pool = ProcessPoolExecutor(5) # 固定产生五个线程
def task(n):
print(os.getpid())
print(n)
time.sleep(1)
return '返回值'
def func(*args, **kwargs):
print('func', args, kwargs)
print(args[0].result())
if __name__ == '__main__':
for i in range(3):
res = pool.submit(task, 233) # 朝池子中提交任务(异步)
print(res.result()) # 同步
# 16928
# 233
# 返回值
# 23472
# 233
# 返回值
# 11156
# 233
# 返回值
'异步任务执行完成后有结果就会自动触发异步回调'
pool.submit(task, 233).add_done_callback(func)
# 13536
# 233
# 10116
# 233
# 22864
# 233
# func (<Future at 0x1acfd9c4400 state=finished returned str>,) {}
# 返回值
# func (<Future at 0x1acfd9e5b20 state=finished returned str>,) {}
# 返回值
# func (<Future at 0x1acfd9e5f70 state=finished returned str>,) {}
# 返回值
协程
'''
协程:单线程下实现并发
在代码层面欺骗CPU,让CPU觉得代码中没有IO操作,实际上IO操作被我们自己写的代码检测 一旦有,立刻让代码执行别的
(该技术完全是程序员自己弄出来的,名字也是程序员自己起的)
核心:自己写代码完成切换+保存状态
'''
import time
from gevent import monkey; monkey.patch_all()
# 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn
def func1():
print('func1 start')
time.sleep(1)
print('func1 over')
def func2():
print('func2 start')
time.sleep(2)
print('func2 over')
if __name__ == '__main__':
start_time = time.time()
s1 = spawn(func1) # 检测代码 一旦有IO自动切换(执行没有io的操作,变向的等待io结束)
s2 = spawn(func2)
s1.join()
s2.join()
print(time.time() - start_time)
# func1 start
# func2 start
# func1 over
# func2 over
# 2.093316078186035
协程实现TCP服务端并发
客户端
import socket
from gevent import monkey; monkey.patch_all()
from gevent import spawn
def communication(sock):
while True:
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper())
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
sock, addr = server.accept()
spawn(communication, sock)
服务端
from threading import Thread, current_thread
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))
def task():
while True:
res = current_thread().name
client.send(res.encode('utf8'))
data = client.recv(1024)
print(data.decode('utf-8'))
for i in range(5):
t = Thread(target=task)
t.start()