死锁指的是某个资源被占用后,一直得不到释放,致使其余须要这个资源的线程进入阻塞状态.python
与普通的区别程序员
解决方法安全
同一个线程必须保证,加锁的次数和解锁的次数相同,其余线程才可以抢到这把锁服务器
能够限制同时并执行公共代码的线程数量网络
若是限制数量为1,则与普通互斥锁没有区别(默认为1)多线程
from threading import Semaphore,current_thread,Thread import time s = Semaphore(2) def task(): s.acquire() time.sleep(1) print(current_thread().name) s.release() for i in range(10): Thread(target=task).start() # 结果是每次都会执行两个子线程
什么是GIL锁?并发
在cpython中,全局解释器锁(GIL,是为了阻止多个本地线程在同一时间执行python字节码的互斥锁,app
由于cpython的内存管理是非线程安全的,这个锁是很是必要的,由于其余愈来愈多的特性依赖这个特性.异步
''' In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) '''
线程安全问题具体的表现函数
cpython解释器与python程序之间的关系:
GC线程: 执行python变量名管理的线程
python会自动帮咱们处理垃圾,清扫垃圾也是一堆代码,所以也须要开启一个线程来执行,这个线程就是GC线程.
而GC线程与咱们程序中的线程就会产生安全问题
例如: 线程a 要定义一个变量
若是在进行到第二步的时候,CPU切换到了GC线程,GC线程就会把这个值当垃圾清理掉,这就会形成线程安全问题.
GIL是一把互斥锁,互斥锁将致使效率下降
具体表现:
在cpython即使开启了多线程,并且CPU也是多核的,却没法并行执行任务,由于解释器只有一个,同一时间只有一个任务在执行.
没办法解决,只能尽量的避免GIL锁影响咱们的效率
使用多进程可以实现并行,从而更好的利用多核CPU
对任务进行区分(分为两类): (重点)
计算密集型:
基本没有IO 大部分时间在计算,例如:人脸识别/图像处理
因为多线程不能 并行,因此应该使用多进程,将任务分给不一样CPU核心
IO密集型:
因为网络IO速度对比CPU处理速度很是慢多线程并不会形成太大的影响
另外若有大量客户端链接服务,进程根本开不起来,只能用多线程
之因此加锁是为了解决线程安全问题,可是有了锁,致使cpython中多线程不能并行,只能并发
可是并不能急就此否定python,有一下几点缘由
性能测试
from multiprocessing import Process from threading import Thread import time # # 计算密集型任务 # # def task(): # for i in range(100000000): # 1+1 # # # if __name__ == '__main__': # start_time = time.time() # # ps = [] # for i in range(5): # p = Process(target=task) # # p = Thread(target=task) # p.start() # ps.append(p) # # for i in ps:i.join() # # print("共耗时:",time.time()-start_time) # 多进程胜 # IO密集型任务 def task(): for i in range(100): with open(r"1.死锁现象.py",encoding="utf-8") as f: f.read() if __name__ == '__main__': start_time = time.time() ps = [] for i in range(10): p = Process(target=task) # p = Thread(target=task) p.start() ps.append(p) for i in ps:i.join() print("共耗时:",time.time()-start_time) # 多线程胜
为何要装到容器中
若是进程不结束,池子里面的进程或者线程也是一直存活的
import os import time from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor from threading import activeCount,enumerate,currentThread # # 建立一个线程池 指定最多能够容纳两个线程 # pool = ThreadPoolExecutor(20) # # def task(): # print(currentThread().name) # # # 提交任务到池子中 # pool.submit(task) # pool.submit(task) # # print(enumerate()) # 进程池的使用 def task(): time.sleep(1) print(os.getpid()) if __name__ == '__main__': pool = ProcessPoolExecutor(2) pool.submit(task) pool.submit(task) pool.submit(task)
同步:
指的是,提交任务后必须在原地等待,直到任务结束. 同步不等于阻塞
异步:
指的是,提交任务后不须要在原地等待,能够继续往下执行代码
异步效率高于同步,异步任务将致使一个问题:就是任务的发起方不知道任务什么时候处理完毕
异步/同步指的是提交任务的方式
解决方法:
轮询: 每隔一段时间就问一次
效率低,没法及时获取结果 (不推荐使用)
异步回调: 让任务的执行方主动通知
能够及时拿到任务的结果,(推荐方式)
# 异步回调 from threading import Thread # 具体的任务 def task(callback): print("run") for i in range(100000000): 1+1 callback("ok") #回调函数 参数为任务的结果 def finished(res): print("任务完成!",res) print("start") t = Thread(target=task,args=(finished,)) t.start() #执行task时 没有致使主线程卡主 而是继续运行 print("over")
线程池中回调的使用
# 使用案例: def task(num): time.sleep(1) print(num) return "hello python" def callback(obj): print(obj.result()) pool = ThreadPoolExecutor() res = pool.submit(task,123) res.add_done_callback(callback) print("over")