死锁现象与递归锁
进程也是有死锁的:
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,
它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,
如下就是死锁
死锁现象:
死锁-------------------
from threading import Thread,Lock,RLock
import time
mutexA = Lock()
mutexB = Lock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('\033[33m%s 拿到A锁 '%self.name)
mutexB.acquire()
print('\033[45%s 拿到B锁 '%self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print('\033[33%s 拿到B锁 ' % self.name)
time.sleep(1) #睡一秒就是为了保证A锁已经被别人那到了
mutexA.acquire()
print('\033[45m%s 拿到B锁 ' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start() #一开启就会去调用run方法
死锁现象
那么怎么解决死锁现象呢?
解决方法,递归锁:在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。
直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁
mutexA=mutexB=threading.RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,<br>则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止
解决死锁
# 2.解决死锁的方法--------------递归锁
from threading import Thread,Lock,RLock
import time
mutexB = mutexA = RLock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('\033[33m%s 拿到A锁 '%self.name)
mutexB.acquire()
print('\033[45%s 拿到B锁 '%self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print('\033[33%s 拿到B锁 ' % self.name)
time.sleep(1) #睡一秒就是为了保证A锁已经被别人拿到了
mutexA.acquire()
print('\033[45m%s 拿到B锁 ' % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start() #一开启就会去调用run方法
解决死锁
信号量Semaphore(其实也是一把锁)
Semaphore管理一个内置的计数器
Semaphore与进程池看起来类似,但是是完全不同的概念。
Semaphore示例:
from threading import Thread,Semaphore,currentThread
import time,random
sm = Semaphore(5) #运行的时候有5个人
def task():
sm.acquire()
print('\033[42m %s上厕所'%currentThread().getName())
time.sleep(random.randint(1,3))
print('\033[31m %s上完厕所走了'%currentThread().getName())
sm.release()
if __name__ == '__main__':
for i in range(20): #开了5个线程 ,这20人都要上厕所
t = Thread(target=task)
t.start()
Semaphore举例
结果:
hread-1上厕所
Thread-2上厕所
Thread-3上厕所
Thread-4上厕所
Thread-5上厕所
Thread-3上完厕所走了
Thread-6上厕所
Thread-1上完厕所走了
Thread-7上厕所
Thread-2上完厕所走了
Thread-8上厕所
Thread-6上完厕所走了
Thread-5上完厕所走了
Thread-4上完厕所走了
Thread-9上厕所
Thread-10上厕所
Thread-11上厕所
Thread-9上完厕所走了
Thread-12上厕所
Thread-7上完厕所走了
Thread-13上厕所
Thread-10上完厕所走了
Thread-8上完厕所走了
Thread-14上厕所
Thread-15上厕所
Thread-12上完厕所走了
Thread-11上完厕所走了
Thread-16上厕所
Thread-17上厕所
Thread-14上完厕所走了
Thread-15上完厕所走了
Thread-17上完厕所走了
Thread-18上厕所
Thread-19上厕所
Thread-20上厕所
Thread-13上完厕所走了
Thread-20上完厕所走了
Thread-16上完厕所走了
Thread-18上完厕所走了
Thread-19上完厕所走了
运行结果
GIL全局解释器锁:
定义:GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
运行py文件内部执行过程
计算机有4个cpu,运行py文件
首先开辟一个进程空间,线程执行将python解释器和文件加载进去,python解释器包含编译器和虚拟机,
文件通过编译器转义成字节码,再通过虚拟机转义成机器码,然后与操作系统去执行该线程
Cpython规定,同一时刻只允许一个线程进入解释器
为什么加锁?
当时都是单核时代,而且cpu价格非常贵
如果不加全局锁,开发Cpython解释器程序员就会在源码内部各种加锁,解锁,非常麻烦,各种死锁现象等等,为了省事直接加了线程锁
优点:保证了Cpython解释器的数据资源安全
缺点:单个进程的多线程不能利用多核
单个进程的多线程可以并发,但是不能利用多核,不能并行,多个进程可以并发,并行
IO密集型和计算密集型
当电脑是三核是执行多个任务,并行执行,遇到阻塞就等待,所以耗费时间比较长
当单核cpu执行多个任务,遇见阻塞,就执行非IO任务,效率较高
IO密集型适合单进程多线程
计算密集型
当电脑是三核的执行多个任务,没有阻塞就执行,效率较高
当电脑是单核的执行多个人来,来回切换执行,例如打开QQ和微信 cpu需要来回切换执行
GIL与lock锁的区别
验证计算密集型和IO密集型的效率
计算密集型
多进程单线程运行速度
from threading import Thread
from multiprocessing import Process
import time
import random
#计算密集型:单个进程的多线程和多个进程并发并行
def task():
count=0
for i in range(10000000):
count+=1
if __name__ == '__main__':
#多进程的并发并行
start_time=time.time()
l1=[]
for i in range(4):
p=Process(target=task,)
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'{time.time()-start_time}')#1.3144586086273193
单进程多线程运行速度
from threading import Thread
from multiprocessing import Process
import time
import random
def task():
count = 0
for i in range(10000000):
count += 1
if __name__ == '__main__':
#单进程多线程
start_time=time.time()
l1=[]
for i in range(4):
p=Thread(target=task,)
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'{time.time()-start_time}')#2.4723618030548096
IO密集型
多进程单单线程运行速度
#IO密集型:单进程的多线程并发 和 多个进程的并发并行
from multiprocessing import Process
import time
import random
def task():
count=0
time.sleep(random.randint(1,3))
count+=1
if __name__ == '__main__':
start_time=time.time()
l1=[]
for i in range(50):
p=Process(target=task,)
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'{time.time()-start_time}')#4.954715013504028
单进程多线程运行速度
from threading import Thread
from multiprocessing import Process
import time
import random
def task():
count=0
time.sleep(random.randint(1,3))
count+=1
if __name__ == '__main__':
start_time=time.time()
l1=[]
for i in range(50):
p=Thread(target=task,)
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'{time.time()-start_time}')#3.013162136077881
多线程实现socke套接字通信
服务端:
import socket
from threading import Thread
def communicate(conn,addr):
while 1:
try:
from_client_data=conn.recv(1024)
print(f'来自客户端{addr}得消息:{from_client_data.decode("utf-8")}')
to_client_data=input(">>>").strip()
conn.send(to_client_data.encode('utf-8'))
except Exception:
break
conn.close()
def _accept():
server=socket.socket()
server.bind(('127.0.0.1',8848))
server.listen(5)
while 1:
conn,addr=server.accept()
t=Thread(target=communicate,args=(conn,addr))
t.start()
if __name__ == '__main__':
_accept()
客户端:
import socket
client=socket.socket()
client.connect(('127.0.0.1',8848))
while 1:
to_server_data=input(">>>>").strip()
client.send(to_server_data.encode('utf-8'))
from_server_date=client.recv(1024)
print(f'来自客户端的消息{from_server_date.decode("utf-8")}')
client.close()
原文:https://www.cnblogs.com/zhangdadayou/p/11431960.html