一堆锁

死锁现象 (重点)

死锁指的是某个资源被占用后,一直得不到释放,导致其他需要这个资源的线程进入阻塞状态.

  • 产生死锁的情况

    • 对同一把互斥锁加了多次
    • 一个共享资源,要访问必须同时具备多把锁,但是这些锁被不同线程或者进程所持有,就会导致相互等待对方释放从而程序就卡死了
  • 第二种情况的解决方法:
    • 抢锁一定按照相同的顺序去抢
    • 给抢锁加上超时,如果超时则放弃执行

递归锁 (了解)

  • 与普通的区别

    • 相同: 多线程之间都有互斥的效果
    • 不同: 同一个线程可以对这个锁执行多次acquire
  • 解决方法

    同一个线程必须保证,加锁的次数和解锁的次数相同,其他线程才能够抢到这把锁

信号量 (了解)

可以限制同时并执行公共代码的线程数量

如果限制数量为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全局锁 (重点)

什么是GIL锁?

在cpython中,全局解释器锁(GIL,是为了阻止多个本地线程在同一时间执行python字节码的互斥锁,

因为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.)
'''

为什么需要GIL锁

  • 线程安全问题具体的表现

    cpython解释器与python程序之间的关系:

    • python程序本质就是一堆字符串,所以运行一个python程序时,必须要开启一个解释器,但是一个python程序中,解释器只有一个,所有代码都要交给它来解释执行;因此当多个线程都要执行代码时就会产生线程安全问题.

cpython解释器与GC线程

GC线程: 执行python变量名管理的线程

python会自动帮我们处理垃圾,清扫垃圾也是一堆代码,因此也需要开启一个线程来执行,这个线程就是GC线程.

而GC线程与我们程序中的线程就会产生安全问题

例如: 线程a 要定义一个变量

  • 先申请一块内存空间
  • 再把数据装进去
  • 最后将引用计数加1

如果在进行到第二步的时候,CPU切换到了GC线程,GC线程就会把这个值当垃圾清理掉,这就会造成线程安全问题.

带来的问题

GIL是一把互斥锁,互斥锁将导致效率降低

具体表现:

在cpython即便开启了多线程,而且CPU也是多核的,却无法并行执行任务,因为解释器只有一个,同一时间只有一个任务在执行.

如何解决 (重点)

没办法解决,只能尽可能的避免GIL锁影响我们的效率

  1. 使用多进程能够实现并行,从而更好的利用多核CPU

  2. 对任务进行区分(分为两类): (重点)

    • 计算密集型:

      基本没有IO 大部分时间在计算,例如:人脸识别/图像处理

      由于多线程不能 并行,所以应该使用多进程,将任务分给不同CPU核心

    • IO密集型:

      由于网络IO速度对比CPU处理速度非常慢多线程并不会造成太大的影响

      另外如有大量客户端连接服务,进程根本开不起来,只能用多线程

关于新能的探讨 (了解)

之所以加锁是为了解决线程安全问题,但是有了锁,导致cpython中多线程不能并行,只能并发

但是并不能急就此否认python,有一下几点原因

  • python是一门语言,而GIL只是cpython解释器的问题,只是python多解释器中的一种
  • 如果是单核CPU,GIL不会造成任何影响
  • 由于目前大多数程序都是基于网络的,网络速度对比CPU是非常慢的,即使CPU也无法提高效率
  • 对于IO密集型任务,不会有太大的影响
  • 如果没有这把锁,我们程序员就必须自己来解决安全问题

性能测试

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) # 多线程胜

GIL锁与自定义锁的区别

  • GIL锁,锁住的是解释器级别的数据
  • 自定义锁,锁的是解释器以外的共享资源,例如:硬盘上的文件/ 控制台,对于这种不属于解释器的数据资源就应该自己加锁处理

线程池与进程池

  • 线程池: 就是装线程的容器
  • 进程池: 就是装进程的容器

为什么要装到容器中

  1. 可以避免频繁的创建和销毁(进行/线程)所造成的资源开销
  2. 可以限制同时存在的线程数量,以保证服务器不会应为资源不足而导致崩溃
  3. 帮我们管理了线程的生命周期
  4. 管理了任务的分配

如果进程不结束,池子里面的进程或者线程也是一直存活的

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")

并发编程-线程-死锁现象-GIL全局锁-线程池的更多相关文章

  1. Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池

    Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密集型效率验证.进程池/线程池 目录 Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密 ...

  2. python 并发编程 多线程 死锁现象与递归锁

    一 死锁现象 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等 ...

  3. 并发编程(五)——GIL全局解释器锁、死锁现象与递归锁、信号量、Event事件、线程queue

    GIL.死锁现象与递归锁.信号量.Event事件.线程queue 一.GIL全局解释器锁 1.什么是全局解释器锁 GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多 ...

  4. 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁

    一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...

  5. python 之 并发编程(守护线程与守护进程的区别、线程互斥锁、死锁现象与递归锁、信号量、GIL全局解释器锁)

    9.94 守护线程与守护进程的区别 1.对主进程来说,运行完毕指的是主进程代码运行完毕2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕​详细解释:1.主 ...

  6. 并发编程(五)--GIL、死锁现象与递归锁、信号量、Event事件、线程queue

    一.GIL全局解释器锁 1.什么是全局解释器锁 GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多个线程,必须抢到GIL之后才能使用Cpython解释器来执行自己的 ...

  7. TCP并发、GIL全局锁、多线程讨论

    TCP实现并发 #client客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while T ...

  8. Python 36 死锁现象和递归锁、信号量、Event事件、线程queue

    一:死锁现象和递归锁 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远 ...

  9. python GIL 全局锁,多核cpu下的多线程性能究竟如何?

    python GIL 全局锁,多核cpu下的多线程性能究竟如何?GIL全称Global Interpreter Lock GIL是什么? 首先需要明确的一点是GIL并不是Python的特性,它是在实现 ...

随机推荐

  1. D3.js的v5版本入门教程(第七章)—— 比例尺的使用

    D3.js的v5版本入门教程(第七章) 比例尺在D3.js中是一个很重要的东西,我们可以这样理解d3.js中的比例尺——一种映射关系,从domain映射到range域(为什么会是domain和rang ...

  2. Net core学习系列(一)——Net Core介绍

    一.什么是Net Core .NET Core是适用于 windows.linux 和 macos 操作系统的免费.开源托管的计算机软件框架,是微软开发的第一个官方版本,具有跨平台 (Windows. ...

  3. 2019软工实践_Alpha(6/6)

    队名:955 组长博客:https://www.cnblogs.com/cclong/p/11913269.html 作业博客:https://edu.cnblogs.com/campus/fzu/S ...

  4. TICK/TIGK运维栈安装运行 docker-compose【下】

    InfluxDB 构建Dockerfilevim /opt/influxdb-docker/Dockerfile FROM influxdb COPY influxdb.conf /etc/influ ...

  5. Devops(四):Docker 镜像管理

    参考 <Docker中上传镜像到docker hub中> <Docker 镜像管理> <通过容器提交镜像(docker commit)以及推送镜像(docker push ...

  6. Redis慢日志查询

    Redis slowlog 是个什么 redis的slow log记录了那些执行时间超过规定时长的请求.执行时间不包括I/O操作(比如与客户端进行网络通信等),只是命令的实际执行时间(期间线程会被阻塞 ...

  7. python离线包下载地址

    https://pypi.org/project/pdfconv/ https://pypi.org/search/?q=major&o=

  8. 初识HDFS(10分钟了解HDFS、NameNode和DataNode)

    概览 首先我们来认识一下HDFS, HDFS(Hadoop Distributed File System )Hadoop分布式文件系统.它其实是将一个大文件分成若干块保存在不同服务器的多个节点中.通 ...

  9. [LeetCode] 295. Find Median from Data Stream 找出数据流的中位数

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  10. SpringBoot 跨域 Access-Control-Allow-Origin

    跨域(CORS)是指不同域名之间相互访问. 跨域,指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略所造成的,是浏览器对于JavaScript所定义的安全限制策略. 只要协议,子域名,主域名, ...