Day37
今日总结
GIL与普通互斥锁的区别
# 验证GIL的存在
from threading import Thread, Lock
import time
money = 100
def task():
global money
money -= 1
for i in range(100): # 创建一百个线程
t = Thread(target=task)
t.start()
print(money) # 0
# 不同数据加不同的锁
from threading import Thread, Lock
import time
money = 100
mutex = Lock()
def task():
global money
mutex.acquire()
tmp = money
time.sleep(0.1)
money = tmp - 1
mutex.release()
t_list = []
for i in range(100): # 创建一百个线程
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join() # 为了确保结构正确 应该等待所有的线程运行完毕再打印money
print(money) # 0
# 在实际工作中几乎不用考虑GIl的存在,GIl是一个纯理论知识,应用面较窄
多线程作用
# 多线程与多进程各有千秋,有不同的适用场景
# 单核
# 多个IO密集型任务
多进程:浪费资源 无法利用多个cpu
多线程:节省资源 切换+保存状态
# 多个计算密集型任务
多进程:耗时更长 创建进程时消耗、切换消耗
所线程:耗时较短 切换消耗
# 多个CPU
# 多个IO密集型任务
多进程:浪费资源 其他CPU闲置
多线程:节省资源 切换+保存状态
# 多个计算密集型任务
多进程:利用多核 速度更快
多线程:速度较慢
# 在多核(12)环境下验证多线程与多进程的适用问题
# 可以通过os模块中的cpu_count()方法查看计算机CPU个数
from threading import Thread
from multiprocessing import Process
import time
# 计算密集型任务
def work():
res = 1
for i in range(1, 10000):
res *= i
if __name__ == '__main__':
start_time = time.time()
# 多进程
p_list = []
for i in range(12):
p = Process(target=work)
p.start()
p_list.append(p)
for p in p_list:
p.join() # 总耗时:0.34558653831481934
# 多线程
t_list = []
for i in range(12):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join() # 总耗时:0.24379849433898926
print('总耗时:%s' % (time.time() - start_time))
# 由上可知,多线程耗时更短
# IO密集型任务
def work():
time.sleep(2)
if __name__ == '__main__':
start_time = time.time()
# 多线程
t_list = []
for i in range(100):
t = Thread(target=work)
t.start()
for t in t_list:
t.join() # 总耗时:0.013962030410766602
# 多进程
p_list = []
for i in range(100):
p = Process(target=work)
p.start()
for p in p_list:
p.join() # 总耗时:1.4700865745544434
print('总耗时:%s' % (time.time() - start_time))
# 由上可知,多线程耗时更短,差距巨大
死锁
# 锁就算掌握了如何抢 如何放 也会产生死锁现象
from threading import Thread, Lock
import time
# 产生两把(复习 面向对象和单例模式):每天都可以写写单例啊 算法啊...
mutexA = Lock()
mutexB = Lock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexB.acquire()
print(f'{self.name}抢到了B锁')
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print(f'{self.name}抢到了B锁')
time.sleep(2)
mutexA.acquire()
print(f'{self.name}抢到了A锁')
mutexA.release()
mutexB.release()
for i in range(20):
t = MyThread()
t.start()
'''
线程1抢到A锁,线程2等着,接着1拿到B锁,释放A锁,等了2s,线程2抢到A锁,B锁没有被及时释放就死锁了
'''
信号量
信息量在不同的知识体系中,功能是不同的:在并发编程中信号量意思是多把互斥锁;在django框架中信号量意思是达到某个条件自动触发特定功能
"""
如果将自定义互斥锁比喻成是一张床
那么信号量相当于是n张床
"""
from threading import Thread, Semaphore
import time
import random
sp = Semaphore(5) # 创建5张床
def task(name):
sp.acquire()
print('%s睡觉ing' % name)
time.sleep(random.randint(1, 5))
sp.release()
for i in range(1, 10):
t = Thread(target=task, args=('第%s号' % i, ))
t.start()
event事件
from threading import Thread, Event
import time
event = Event() # 类似于造了一个红绿灯
def light():
print('红灯')
time.sleep(3)
print('绿灯')
event.set()
def car(name):
print('%s正在等红灯' % name)
event.wait()
print('%s绿地通过了' % name)
t = Thread(target=light)
t.start()
for i in range(20):
t = Thread(target=car, args=('第%s号' % i,))
t.start()
进程池与线程池
'''
池:
保证计算机硬件安全的情况下提升程序的运行效率
进程池:
提前创建好固定数量的进程,后续反复使用这些进程
线程池:
提前创建好固定数量的线程,后续反复使用这些线程
如果任务超出了池子里面的最大进程或线程数,则原地等待
进程池和线程池虽然降低了程序的运行效率,但是保证了硬件的安全
'''
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
# 线程池
pool = ThreadPoolExecutor(5) # 创建五个待命的线程,其中参数可以自定义,默认是CPU个数*5
# 异步回调机制
pool.submit(task, i).add_done_callback(func) # 自动提交提醒
# 进程池
pool = ProcessPoolExecutor(5) # 创建五个待命的进程,其中参数可以自定义,默认是CPU的个数
# 异步回调机制
pool.submit(task, i).add_done_callback(func)
协程
# 操作系统只认识进程与线程,协程是认为臆想的名词,作用:单线程下实现并发,通过代码来检测程序的IO操作并自己处理 让CPU感觉不到IO的存在从而最大幅度的占用CPU
from gevent import monkey;monkey.patch_all() # 固定格式 用于检测所有的IO操作
from gevent import spawn
import time
def func1():
print('1 is running')
time.sleep(5)
print('1 is over' )
def func2():
print('2 is running' )
time.sleep(3)
print('2 is over' )
start_time = time.time()
g1 = spawn(func1)
g2 = spawn(func2)
g1.join() # 等待检测任务执行完毕
g2.join()
print('总耗时:', time.time() - start_time) # 据经验,串行耗时一定是8s+
# 总耗时: 5.031261682510376 实际却是并发
基于协程实现TCP服务端并发
from gevent import monkey;monkey.patch_all()
from gevent import spawn
import socket
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)
g1 = spawn(get_server)
g1.join()
'''
python可以通过开设多进程 在多进程下开设多线程,在多线程使用协程,从而让程序执行的效率达到极致
但是实际业务中很少需要这么高的效率,因为大部分程序都是IO密集型的,所以协程了解即可
'''
网络编程结束啦,感觉收获良多,明天就学前端了,希望我还没忘光以前学的前端吧


浙公网安备 33010602011771号