死锁与递归锁
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺 资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
死锁
from threading import Thread,Lock,current_thread,RLock
import time
mutexA = Lock()
mutexB = Lock()
# mutexA = mutexB = RLock()
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(10):
t = MyThread()
t.start()
Thread-1抢到了A锁
Thread-1抢到了B锁
Thread-1释放了B锁
Thread-1释放了A锁
Thread-1抢到了B锁
Thread-2抢到了A锁
程序卡在这里,这就是死锁现象
解决方法:递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
RLock可以提供被第一个抢到锁的人连续的acquire和release
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0,其他人都不能抢。
递归锁RLock
from threading import Thread,Lock,current_thread,RLock
import time
# mutexA = Lock()
# mutexB = Lock()
mutexA = mutexB = RLock() # A B 是用一把锁
class MyThread(Thread):
# 创建线程自动触发run方法,run方法内调用func1 func2相当于也是自动触发
def run(self):
self.func1()
self.func2()
def func1(self):
mutexA.acquire()
# self.name等价于current_thread().name
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(10):
t = MyThread()
t.start()
Thread-1抢到了A锁
Thread-1抢到了B锁
Thread-1释放了B锁
Thread-1释放了A锁Thread-2抢到了A锁
Thread-2抢到了B锁
Thread-2释放了B锁
Thread-2释放了A锁Thread-3抢到了A锁
Thread-3抢到了B锁
Thread-3释放了B锁
Thread-3释放了A锁Thread-1抢到了B锁
Thread-1抢到了A锁
Thread-1释放了A锁
Thread-1释放了B锁
Thread-4抢到了A锁
Thread-4抢到了B锁
Thread-4释放了B锁
Thread-4释放了A锁
Thread-2抢到了B锁
Thread-2抢到了A锁
Thread-2释放了A锁
Thread-2释放了B锁
Thread-5抢到了A锁
Thread-5抢到了B锁
Thread-5释放了B锁
Thread-5释放了A锁
Thread-3抢到了B锁
Thread-3抢到了A锁
Thread-3释放了A锁
Thread-3释放了B锁
Thread-6抢到了A锁
Thread-6抢到了B锁
Thread-6释放了B锁
Thread-6释放了A锁Thread-7抢到了A锁
Thread-7抢到了B锁
Thread-7释放了B锁
Thread-7释放了A锁Thread-8抢到了A锁
Thread-8抢到了B锁
Thread-8释放了B锁
Thread-8释放了A锁
Thread-9抢到了A锁
Thread-9抢到了B锁
Thread-9释放了B锁
Thread-9释放了A锁Thread-10抢到了A锁
Thread-10抢到了B锁
Thread-10释放了B锁
Thread-10释放了A锁Thread-4抢到了B锁
Thread-4抢到了A锁
Thread-4释放了A锁
Thread-4释放了B锁
Thread-5抢到了B锁
Thread-5抢到了A锁
Thread-5释放了A锁
Thread-5释放了B锁Thread-7抢到了B锁
Thread-7抢到了A锁
Thread-7释放了A锁
Thread-7释放了B锁Thread-6抢到了B锁
Thread-6抢到了A锁
Thread-6释放了A锁
Thread-6释放了B锁Thread-8抢到了B锁
Thread-8抢到了A锁
Thread-8释放了A锁
Thread-8释放了B锁Thread-9抢到了B锁
Thread-9抢到了A锁
Thread-9释放了A锁
Thread-9释放了B锁Thread-10抢到了B锁
Thread-10抢到了A锁
Thread-10释放了A锁
Thread-10释放了B锁
只要类夹括号实例化对象,无论传入的参数是否一样生成的对象肯定不一样,单例模式除外。还有自己千万不要轻易的处理锁的问题。
class Demo(object):
pass
obj1 = Demo()
obj2 = Demo()
print(id(obj1),id(obj2)) # 41177456 41177424
信号量
信号量可能在不同的领域中,对应不同的知识点。
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
0占了一个位置
1占了一个位置
2占了一个位置
3占了一个位置
4占了一个位置
5占了一个位置
6占了一个位置7占了一个位置
9占了一个位置8占了一个位置
10占了一个位置
11占了一个位置
12占了一个位置
13占了一个位置
14占了一个位置
15占了一个位置
16占了一个位置
17占了一个位置
18占了一个位置
20占了一个位置
19占了一个位置
22占了一个位置21占了一个位置
23占了一个位置24占了一个位置
25占了一个位置
26占了一个位置
27占了一个位置
28占了一个位置
29占了一个位置
30占了一个位置
32占了一个位置31占了一个位置
33占了一个位置
34占了一个位置
35占了一个位置
36占了一个位置37占了一个位置
38占了一个位置
39占了一个位置
event事件
from threading import Event,Thread
import time
# 先生成一个event对象
e = Event()
def light():
print("红灯正亮着")
time.sleep(3)
e.set() # 发信号
print("绿灯亮了")
def car(name):
print(f"{name}正在等红灯")
e.wait() # 等待信号
print(f"{name}加油门飙车了")
t = Thread(target=light)
t.start()
for i in range(10):
t = Thread(target=car,args=(f"伞兵{i}",))
t.start()
红灯正亮着
伞兵0正在等红灯
伞兵1正在等红灯
伞兵2正在等红灯
伞兵3正在等红灯
伞兵4正在等红灯
伞兵5正在等红灯
伞兵6正在等红灯
伞兵7正在等红灯
伞兵8正在等红灯
伞兵9正在等红灯
绿灯亮了伞兵0加油门飙车了
伞兵1加油门飙车了
伞兵5加油门飙车了
伞兵6加油门飙车了伞兵8加油门飙车了
伞兵9加油门飙车了
伞兵2加油门飙车了
伞兵4加油门飙车了伞兵7加油门飙车了
伞兵3加油门飙车了
线程
同一个进程下的多个线程本来就是数据共享,为什么还要用队列呢?
因为队列是管道+锁 使用队列你就不需要自己手动操作锁的问题
因为锁操作的不好极容易产生死锁现象。
import queue
q = queue.Queue()
q.put("hahaha")
print(q.get())
# 后进先出
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get()) # 3
# 优先级对列,数字越小,优先级越高
q = queue.PriorityQueue()
q.put((10,'hahaha'))
q.put((100,"heheheh"))
q.put((0,"xxx"))
q.put((-10,"vvvv"))
print(q.get())
TCP使用线程实现并发效果
# 客户端
import socket
client = socket.socket()
client.connect(("127.0.0.1",8080))
while True:
client.send(b'hello')
data = client.recv(1024)
print(data.decode('utf-8'))
# 服务端
"""
服务端:
1.要有固定的IP和PORT
2.24小时不间断提供服务
3.能够支持并发
"""
import socket
from threading import Thread
server = socket.socket()
server.bind(("127.0.0.1",8080))
server.listen(5)
def talk(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break
print(data.decode('utf-8'))
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close()
while True:
# 等待客户端的连接 阻塞态
conn,addr = server.accept()
print(addr)
t = Thread(target=talk,args=(conn,))
t.start()