一、GIL与普通互斥锁区别
from threading import Thread
money = 100
def fask():
global money
money -= 1
for i in range(100):
t = Thread(target=fask)
t.start()
print(money) # 0
通过上面的操作验证了GIL全局解释器锁是存在的
from threading import Thread, Lock
import time
money = 100
mutex = Lock()
def fask():
global money
mutex.acquire()
num = money
time.sleep(0.1)
money = num - 1
mutex.release()
t_list = []
for i in range(100):
t = Thread(target=fask)
t.start()
t_list.append(t)
for i in t_list:
i.join()
print(money) # 0
二、死锁现象
当多个线程同时需要某一个锁的资源时,无法获取锁的资源会等待获取,多个线程都互相等待对方锁的资源,就会产生死锁现象。
def play(num):
mutexA.acquire()
print("子进程%d获取A锁"%num)
time.sleep(3)
mutexB.acquire()
print("子进程%d获取B锁"%num)
mutexB.release()
print("子进程%d释放B锁"%num)
mutexA.release()
print("子进程%d释放A锁" % num)
def play1(num):
mutexB.acquire()
print("子进程%d获取B锁"%num)
time.sleep(3)
mutexA.acquire()
print("子进程%d获取A锁" % num)
mutexA.release()
print("子进程%d释放A锁"%num)
mutexB.release()
print("子进程%d释放B锁"%num)
t = Thread(target=play, args=(1, ))
t1 = Thread(target=play1, args=(2, ))
t.start()
t1.start()
三、信号量
信号量在不同的知识体系中 展示出来的功能是不一样的
在互斥锁中展现为多个可以被多个资源获取的互斥锁
同一时刻只有三个线程可以使用fask方法创建并开启进程
from threading import Thread, Semaphore
import time
import random
sp = Semaphore(3)
def fask(num):
sp.acquire()
print("%d号子弹飞出"%num)
time.sleep(random.randint(1, 2))
sp.release()
print("%d子弹命中"%num)
for i in range(20):
t = Thread(target=fask, args=(i, ))
t.start()
四、event事件
一般情况下子线程的运作是与主线程有关的,但使用了event后可以让子线程决定子线程的运行状况
from threading import Thread, Event
import time
eventO = Event()
def wait_time():
print("三秒后即将开始抢玻璃")
time.sleep(3)
print("三秒到开始抢玻璃")
eventO.set()
def get_glass(num):
print("%d号选手正在参与抢玻璃!"%num)
eventO.wait()
print("%d号选手抢到了玻璃!"%num)
t = Thread(target=wait_time)
t.start()
for i in range(1, 31):
t1 = Thread(target=get_glass, args=(i, ))
t1.start()
五、进程池
计算机硬件是有物理极限的 我们不可能无限制的创建进程
进程池保证计算机硬件安全的情况下提升程序的运行效率,提前创建好固定数量的进程 后续反复使用这些进程,如果任务超出了池子里面的最大进程数 则原地等待
进程池其实降低了程序的运行效率 但是保证了硬件的安全
from concurrent.futures import ProcessPoolExecutor
import time
def make_pool(num):
print("%d号子进程正在被处理"%num)
start_time = time.strftime("%Y %m %d %X")
print(f"开始时间{start_time}")
time.sleep(1)
def func(*args, **kwargs):
args[0].result()
if __name__ == '__main__':
pool = ProcessPoolExecutor(2)
for i in range(1, 11):
pool.submit(make_pool, i).add_done_callback(func)
六、线程池
计算机硬件是有物理极限的 我们不可能无限制的创建线程
线程池保证计算机硬件安全的情况下提升程序的运行效率,提前创建好固定数量的线程 后续反复使用这些线程,如果任务超出了池子里面的最大线程数 则原地等待
线程池其实降低了程序的运行效率 但是保证了硬件的安全
from concurrent.futures import ThreadPoolExecutor
import time
def make_pool(num):
print("%d号子线程正在被处理"%num)
start_time = time.strftime("%Y %m %d %X")
print(f"开始时间{start_time}")
time.sleep(1)
def func(*args, **kwargs):
args[0].result()
pool = ThreadPoolExecutor(2)
for i in range(1, 11):
pool.submit(make_pool, i).add_done_callback(func)
七、协程
协程的主要目的是在单线程下完成并发,其本质是在于不断切换状态,在多个需要资源的代码之间来回服务。协程就是自己通过代码来检测程序的IO操作并自己处理 让CPU感觉不到IO的存在从而最大幅度的占用CPU。
from gevent import monkey;monkey.patch_all() # 固定编写 用于检测所有的IO操作
from gevent import spawn
import time
def play(name):
print('%s play 1' % name)
time.sleep(5)
print('%s play 2' % name)
def eat(name):
print('%s eat 1' % name)
time.sleep(3)
print('%s eat 2' % name)
start_time = time.time()
g1 = spawn(play, 'jason')
g2 = spawn(eat, 'jason')
g1.join() # 等待检测任务执行完毕
g2.join() # 等待检测任务执行完毕
print('总耗时:', time.time() - start_time)
协程完成服务器与客户端的信息交互
服务器
from gevent import monkey;monkey.patch_all()
from gevent import spawn
import socket
def create_server():
server = socket.socket()
server.bind(("127.0.0.1", 8080))
server.listen(5)
while True:
sock, addr = server.accept()
spawn(communication, sock, addr)
def communication(sock, addr):
while True:
data = sock.recv(1024)
print(f"{addr}客户端:{data.decode('utf8')}")
sock.send("服务器收到消息".encode("utf8"))
spawn(create_server).join()
客户端
import socket
client = socket.socket()
client.connect(("127.0.0.1", 8080))
while True:
client.send("协程信息测试".encode("utf8"))
data = client.recv(1024)
print(f"服务器:{data.decode('utf8')}")