验证GIL的存在
- GIL的存在使得多个线程不能同时进行
from threading import Thread
num = 99
def sack():
global num
num -= 1
t_list = []
for i in range(99):
t = Thread(target=sack)
t.start()
t_list.append(t) # 将创建的线程保存到列表中
for t in t_list:
t.join() # 主线程等待子线程结束后运行
print(num) # 0
验证GIL的特点
1.GIL的特点:只在Cpython解释器层面起作用,防止数据被Cpython解释器中的垃圾回收机制线程不安全性,不会影响程序层面的数据操作,如果要保证安全性需要自己加锁(GIL类似于大门锁不保证房间的安全性)
from threading import Thread
import time
num = 99
def sack():
global num
tep = num # 拿到数据
time.sleep(0.1) # io操作会使所有的子线程都拿到数据值99,再执行后续操作 结果为98
num = tep-1
t_list = []
for i in range(99):
t = Thread(target=sack)
t.start()
t_list.append(t) # 将创建的线程保存到列表中
for t in t_list:
t.join() # 主线程等待子线程结束后运行
print(num) # 98
2.想要保证程序层面数据的安全性需要加互斥锁
from threading import Thread, Lock
import time
num = 99
l = Lock() # 加把互斥锁
def sack():
l.acquire() # 抢锁
global num
tep = num # 拿到数据
time.sleep(0.1) # 加锁后io操作也只会使一个子线程都拿到数据值99,执行减一之后释放锁,后续子线程也实行相同操作 结果为0
num = tep-1
l.release() # 释放锁
t_list = []
for i in range(99):
t = Thread(target=sack)
t.start()
t_list.append(t) # 将创建的线程保存到列表中
for t in t_list:
t.join() # 主线程等待子线程结束后运行
print(num) # 0
python中不同情况下的多进程与多线程
1.单CPU
1.1 IO密集型多进程与IO密集型多线程
IO密集型多进程:申请额外的空间,消耗更多的资源
IO密集型多线程:资源消耗较少,通过多道技术
1.2 计算密集型多进程与计算密集型多线程
计算密集型多进程:总耗时+申请空间+切换时间+拷贝代码
计算密集型多线程:总耗时+切换时间
2.多CPU
2.1 IO密集型多进程与IO密集型多线程
IO密集型多进程:单个进程耗时+IO切换时间+申请空间
IO密集型多线程:单个线程耗时+IO切换时间
from multiprocessing import Process
from threading import Thread
import os
import time
# 多进程
# IO密集型
def work():
time.sleep(2)
if __name__ == '__main__':
num_list = []
print(os.cpu_count()) # 查看CPU的个数8
start_time = time.time()
for i in range(100):
p = Process(target=work)
p.start()
num_list.append(p)
for i in range(100):
t = Thread(target=work)
t.start()
num_list.append(t)
for p in num_list:
p.join()
#
print(f'多进程总耗时:{time.time() - start_time}') # 多进程总耗时:3.892953634262085
print(f'多线程总耗时:{time.time() - start_time}') # 多线程总耗时:2.028594493865967
2.2 计算密集型多进程与计算密集型多线程
计算密集型多进程:单个进程时间
计算密集型多线程:多个线程运行时间之和
from multiprocessing import Process
from threading import Thread
import os
import time
# 多进程
# 计算密集型
def work():
num = 1
for i in range(1, 100000):
num *= i
if __name__ == '__main__':
num_list = []
print(os.cpu_count()) # 查看CPU的个数8
start_time = time.time()
for i in range(8):
p = Process(target=work)
p.start()
num_list.append(p)
for i in range(8):
t = Thread(target=work)
t.start()
num_list.append(t)
for p in num_list:
p.join()
print(f'多进程总耗时:{time.time() - start_time}') # 多进程总耗时:5.616981029510498
print(f'多线程总耗时:{time.time() - start_time}') # 多线程总耗时:22.35317635536194
死锁现象
from threading import Thread, Lock
import time
l1 = Lock()
l2 = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
l1.acquire()
print('func1抢l1')
l2.acquire()
print('func1抢l2')
l2.release()
print('func1释放l2')
l1.release()
print('func1释放l1')
def func2(self):
l2.acquire()
print('func2抢l2')
l1.acquire()
time.sleep(2)
print('func2抢l1')
l1.release()
print('func2释放l1')
l2.release()
print('func2释放l2')
for i in range(10):
t = MyThread()
t.start()
信号量
信号量也是互斥锁(不过是多把锁)通过Semaphore类获得不同数量的锁
强调:
信号量在不同的知识体系中 意思可能有区别
在并发编程中 信号量就是多把互斥锁
在django中 信号量指的是达到某个条件自动触发(中间件)
from threading import Thread, Semaphore
import random
import time
sp = Semaphore(5)
class MyThread(Thread):
def run(self):
sp.acquire()
print(self.name) # 第一次5个线程抢到5把锁,之后根据释放时间不同被不同的线程抢到不同数量的锁
time.sleep(random.randint(1, 4))
sp.release()
for i in range(20):
t = MyThread()
t.start()
event事件
子进程与子进程之间相互等待,当一个子进程执行到某个位置通知另一个子进程执行
from threading import Thread, Event
import time
event = Event()
def signal():
print('红灯亮啦,请等一等') # 1
time.sleep(5)
print('绿灯亮了,可以出发啦') # 3
event.set()
def car():
print('红灯等待中') # 2
event.wait()
print('绿灯啦,出发') # 4
t = Thread(target=signal)
t.start()
for i in range(10):
t = Thread(target=car)
t.start()
进程池与线程池
1.产生原因:防止内存溢出及计算机硬件安全,因此多线程与多进程是不能无限创建的
2.池:降低程序的执行效率,保证计算机硬件安全
进程池:提前创建好固定个数的进程数供程序使用,后续不会再创建
线程池:提前创建好固定个数的线程数供程序使用,后续不再创建
submit(函数名,实参1,实参2,...)
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from threading import current_thread
import os
import time
# pool = ThreadPoolExecutor(5) # 固定产生五个线程
pool = ProcessPoolExecutor(5) # 固定产生五个线程
def task(n):
# print(current_thread().name)
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(20):
# res = pool.submit(task,123) # 朝池子中提交任务(异步)
# print(res.result()) # 同步
# pool.submit(task, 123).add_done_callback(func)
"""异步回调:异步任务执行完成后有结果就会自动触发该机制"""
pool.submit(task, 123).add_done_callback(func)
协程
进程:资源单位
线程:执行单位
协程:单线程下实现并发(效率极高)
在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作
实际上IO操作被我们自己写的代码检测 一旦有 立刻让代码执行别的
(该技术完全是程序员自己弄出来的 名字也是程序员自己起的)
核心:自己写代码完成切换+保存状态
from gevent import monkey
from gevent import spawn
import time
monkey.patch_all() # 固定写法
def func1():
print('func1运行')
time.sleep(3)
print('func1运行结束')
def func2():
print('func2运行')
time.sleep(4)
print('func2运行结束')
if __name__ == '__main__':
start = time.time()
s = spawn(func1)
s1 = spawn(func2)
s.join()
s1.join()
print(time.time()-start) # 4.0318992137908936
协程实现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', 8090))
server.listen(5)
while True:
sock, address = server.accept()
spawn(communication, sock)
s1 = spawn(get_server)
s1.join()
客户端:
import socket
def get_client():
client = socket.socket()
client.connect(('127.0.0.1', 8090))
while True:
client.send(f'helloword{current_thread().name}'.encode('utf8'))
date = client.recv(1024)
print(date.decode('utf8'))
for i in range(400):
t = Thread(target=get_client)
t.start()
posted on
浙公网安备 33010602011771号