目录
一、死锁与递归锁(了解)
1 死锁
程序中如果出现多个锁操作容易造成死锁现象
from threading import Thread,Lock
import time
# 实例化两个锁对象
a = Lock()
b = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
a.acquire() # 10个线程去抢和这个锁
print(self.name)
b.acquire() # 抢到a的才能抢b
print(self.name)
b.release()
a.release() # 施放a的时候 线程2瞬间抢到了a开始执行
def func2(self):
b.acquire() # 线程1抢到b和线程2互相卡死,因为需要对方的锁才能施放自己的锁
print(self.name)
time.sleep(2)
a.acquire()
print(self.name)
a.release()
b.release()
for i in range(10):
t = MyThread()
t.start()
2 递归锁
"""
递归锁的特点
可以被连续的acquire和release
但是只能被第一个抢到这把锁执行上述操作
它的内部有一个计数器 每acquire一次计数加一 每realse一次计数减一
只要计数不为0 那么其他人都无法抢到该锁
"""
# 将上述的
mutexA = Lock()
mutexB = Lock()
# 换成
mutexA = mutexB = RLock()
二、信息量
信号量在不同的阶段可能对应不同的技术点
在并发编程中信号量指的是锁
from threading import Thread, Semaphore
import time
import random
sm = Semaphore(5) # 括号内写数字 写几就表示开设几个坑位
# 内置方法和锁一样,抢和施放
# 区别在于信号量可以规定一次性抢锁的最大数量
def task(name):
sm.acquire()
print('%s 正在蹲坑'% name)
time.sleep(random.randint(1, 5))
sm.release()
if __name__ == '__main__':
for i in range(20):
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)
if __name__ == '__main__':
t = Thread(target=light)
t.start()
for i in range(20):
t = Thread(target=car, args=('%s'%i, ))
t.start()
四、三种优先级数据操作
同一个进程下多个线程数据共享,但为了保证数据安全,我们需要用到以下三种数据操作
1 队列
原则:先进先出
import queue
q = queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.empty()) # 判断队列是否为空
print(q.full()) # 判断队列是否为满
q.get()
q.get()
q.get()
q.get(timeout=3) # 等待3秒再取数据,如果没有就报错
q.get_nowait() # 不等待,没有数据就报错
2 堆栈
原则:先进后出
堆栈
import queue
q = queue.LifoQueue(3) # last in first out
q.put(1)
q.put(2)
q.put(3)
print(q.get())
>>> 3
3 自定义优先级
import queue
q = queue.PriorityQueue(3) # 上传数据时要先上传一个该数字排名,排名小的现出来
q.put(('5',1))
q.put(('0',1))
q.put(('-1',1))
print(q.get_nowait())
>>> ('-1',1)
五、进程池和线程池
之前在tcp实现并发的时候我们是来一个用户就启用一个线程或者进程去为用户服务
但是计算机的硬件资源目前还无法支持我们无限制的开发线程
所以用到了池的概念
池相当于缓冲地带,让我们可以保证计算机在正常运行的情况下最大限度的利用它
它降低了程序的运行效率,但是保证了计算机的安全
如果把计算机比喻成一个小池子你限定了池子最大容量是1,你要喂食,池子就会先产生一条鱼,给你喂,其他人想喂就得等你结束,过来喂的是同一条鱼
而如果不用池的概念去限定,每个人来池子都会给人产生一条鱼来服务,鱼多了,池子就炸了。
基本使用
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
import os
# pool = ThreadPoolExecutor(5)# 线程池里只有5个线程
pool = ProcessPoolExecutor(2)# 进程池里只有5个进程
# 括号内可以传数字 不传的话默认会开设当前计算机cpu个数五倍的线程
# 池子造出来之后 里面会固定存在五个线程
# 这个五个线程不会出现重复创建和销毁的过程
# 池子的使用非常的简单
# 你只需要将需要做的任务往池子中提交即可 自动会有人来服务你
def text(n):
print(os.getpid())
time.sleep(3)
return n**n
def callback(n):
print(n.result())
if __name__ == '__main__':
res = pool.submit(text,2) # 返回值是一个对象
# print('主') # 异步提交,判断异步的方式,在任务提交后得到返回值之前能不能执行代码
# print(res.result()) # 得到的是提交任务的返回值
t_list = []
for i in range(10):
pool.submit(text,i).add_done_callback(callback) # 自定义异步回调机制,在任务得到返回值的时候直接调用里面的函数
# 这种回调机制可以想象为,在提交任务后,直接运行下面的代码,什么时候有返回值了,会立刻调用该方法,相当于埋下了定时炸弹,返回值来了就触发
# res = pool.submit(text,i).result() # 这种得到结果的方式是同步
# t_list.append(res)
# pool.shutdown() # 关闭所有线程
# for i in t_list:
# print(i)
六、协程
- 进程:资源单位
- 线程:执行单位
- 协程:意淫出来的
- 为了在多线程实现并发,我们在多线程中出现io情况都切换走,去执行其他线程,这样就会让计算机看起来一直在运行没有中断过。从而提升了程序的运行效率
七、gevent模块
from gevent import monkey;monkey.patch_all()
# 猴子补丁,用来检测程序里出现的io操作
import time
from gevent import spawn
def a():
print('a')
time.sleep(2)
print('a end')
def b():
print('b')
time.sleep(4)
print('b end')
start = time.time()
g1 = spawn(a)
g2 = spawn(b)
g1.join()
g2.join()# 等待任务执行完再执行后面的代码
print(time.time()-start) # 4.015194654464722 实现了io时间切换操作,用户只需等待最长的任务时间
八、协程实现tcp并发
#服务端
import socket
from gevent import monkey;monkey.patch_all()
from gevent import spawn
def communication(conn):
while True:
try:
msg = conn.recv(1024)
if len(msg) == 0:break
conn.send(msg.upper())
except Exception as e:
print(e)
break
conn.close()
def sever(ip,port):
s = socket.socket()
s.bind((ip, port))
s.listen(5)
while True:
conn,addr = s.accept()
communication(conn)
if __name__ == '__main__':
g = spawn(sever,'127.0.0.1',8080)
g.join()
# 客户端
import socket
from threading import Thread,current_thread
def client():
s = socket.socket()
s.connect(('127.0.0.1', 8080))
n = 0
while True:
msg = '%s say hello %s' % (current_thread().name, n)
n += 1
s.send(msg.encode('utf-8'))
data = s.recv(1024)
print(data.decode('utf-8'))
if __name__ == '__main__':
for i in range(50):
t = Thread(target=client)
t.start()