并发编程基础03
死锁与递归锁
我们呢在使用锁的时候,都是一个acquire对应一个release。线程躲着进程之间先抢锁,然后使用完毕之后在释放锁,如果出现进程1拿走了A锁,然后去抢B锁,B锁却被进程2抢走,然后进程2需要使用A锁,这个时候就出现了死锁现象,程序会被卡住。
解决这个问题就可以用递归锁。
递归锁可以被连续抢锁和释放锁。其内部有一个计数器,acquire一次就+1,release一次就-1。只要计数不为0,那么就不会有人抢锁。
from threading import RLock
mutexA = mutexB = RLock()
信号量
信号量在并发编程中指的是锁的数量。主要是利用Semaphore模块来实现的。
正常的给数据上锁之后,同一时刻只有一个线程可以使用,但是如果是多个信号量的话,就可以有多个线程同时进行使用。
from threading import Thread, Semaphore
sm = Semaphore(3) # 表示一个可以由3个用户同一时刻一起使用的锁。
Event事件
线程或者进程需要等待另一个线程或进程运行完毕返回结果之后才能运行。
from threading import Event
event = Event()
event.set() # 发送信号给wait。
event.wait() # 在没有接到set信号之前,是不运行下面的代码的。
线程q
同一进程下的多个线程的数据是共享的,在同一个进程之下使用队列是为了保证同一进程下的数据安全。而队列有三种类型。
import queue
# 1.队列q,先进先出。
q = queue.Queue()
# 2.后进先出队列q
q = queue.LifoQueue() # last in first out
# 3.优先级q。
q = queue.PriorityQueue()
q.put((1,"a")) # 接受的是元祖作为参数,第一个值为优先级,取值的时候,优先级小的先出
进程池与线程池
进程和线程的开设都是需要消耗资源的,我们不可能无限制的去开设线程或者进程,而进程池和线程池就是可以限制开设进程或线程的数量,最大限度的使用计算机硬件。
池的意义:在保证计算机硬件安全的情况下,最大限度的利用计算机。
作用是降低了效率但是保证计算机硬件的安全,让程序能够正常运行。
from concurrent.futures import ThreadPoolExector,ProcessPoolExector
# 在定义时候就固定产生3个线程,相当于3个服务员,不能开除或者在招人。
pool = ThreadPoolExector(3) # 数字不填默认为cpu个数*5
# 同线程
pool = ProcessPoolExector(3) # 数字不填默认为cpu个数
res = pool.submit(task,i) # 提交任务给池子,由池子中的服务员服务任务。res是一个对象属性
提交任务的方式为异步提交,那么怎么得到异步任务的返回结果呢?
# 异步回调机制
# 将服务员完成的结果通过add_done.call_back方法,传进call_back自定义函数内部处理。
res = pool.submit(task,i).add_done.call_back(call_back)
总结:
- 程序的并发变成了串行。
- 任务为什么打印的是None
- res.result拿到的就是异步提交任务的返回结果。
# 导入模块
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# 创建池子
pool = ProcessPoolExecutor(5)
# 通过异步回调机制拿到任务结果
pool.submit(task, i).add_done_callback(call_back)
协程
进程是资源单位,线程是执行单位。
协程:单线程实现并发。
多道技术是进程之间并发效果,而协程是将线程进行切分实现并发效果。
首先,当程序遇到IO操作和长时间占用CPU就会被剥夺cpu使用权,那么当线程在执行任务遇到上述操作的时候,被检测到然后剥夺使用权而进行下一任务。这样就实现了线程的并发效果。
实现协程是依靠程序员在代码级别实现的,主要是利用生成器将任务挂起。但是这样会增加来回切换的时间,同时还可以使用gevent模块来实现检测IO操作。
对于计算密集型,切换是降低效率的,但是对于IO密集型,可以提高工作效率。
# gevent模块导入有一个固定的套路。
from gevent import monkey;monkey.patch_all()
from gevent import spawn
g1 = spawn(被检测函数)
g1.join() # 等被检测任务执行完毕之后,在向下执行
理想状态:多进程》多线程》协程程序。通过上述顺序来提升执行效率。