6-[多线程]-互斥锁、GIL、死锁、递归锁、信号量
1、互斥锁(排他锁)
(1)不加锁的情况下
并发控制问题:多个事务并发执行,可能产生操作冲突,出现下面的3种情况
- 丢失修改错误
- 不能重复读错误
- 读脏数据错误
# mutex
from threading import Thread
import time n = 100 def task():
global n
temp = n
time.sleep(0.1)
n = temp - 1
print(n) if __name__ == '__main__':
t_l = []
for i in range(100):
t = Thread(target=task)
t_l.append(t)
t.start() for t in t_l:
t.join() print('主done', n)
from threading import Thread,Lock
import os,time
def work():
global n
lock.acquire()
temp=n
time.sleep(0.1)
n=temp-1
lock.release()
if __name__ == '__main__':
lock=Lock()
n=100
l=[]
for i in range(100):
p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join() print(n) #结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全,不加锁则结果可能为99
(2)、添加互斥锁(排他锁)
锁: 排它锁(可以修改数据)
共享锁(只可以读数据)
2、GIL全局解释器锁
参考博客:http://www.cnblogs.com/venicid/p/7975892.html
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
可以肯定的一点是:保护不同的数据的安全,就应该加不同的锁。
在一个python的进程内,不仅有test.py的主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一个进程内,毫无疑问
1、所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码)
例如:test.py定义一个函数work(代码内容如下图),在进程内所有线程都能访问到work的代码,于是我们可以开启三个线程然后target都指向该代码,能访问到意味着就是可以执行。 2、所有线程的任务,都需要将任务的代码当做参数传给解释器的代码去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器的代码。
3、GIL与Lock
GIL 与Lock是两把锁,保护的数据不一样,
前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),
后者是保护用户自己开发的应用程序的数据
1、100个线程去抢GIL锁,即抢执行权限
2、肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire()
3、极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL
4、直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
4、GIL与多线程
要解决这个问题,我们需要在几个点上达成一致: 1、cpu到底是用来做计算的,还是用来做I/O的? 2、多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能 3、每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处
结论: 1、对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用
2、当然对运行一个程序来说,随着cpu的增多执行效率肯定会有所提高(不管提高幅度多大,总会有所提高),这是因为一个程序基本上不会是纯计算或者纯I/O,
所以我们只能相对的去看一个程序到底是计算密集型还是I/O密集型,从而进一步分析python的多线程到底有无用武之地
5、多线程性能测试
计算密集型、io密集型
- 如果并发的多个任务是计算密集型:多进程效率高
# 计算密集型:多进程执行效率高 from multiprocessing import Process
from threading import Thread
import time
import os def task():
print('子 is running')
ret = 0
for i in range(10000000):
ret *= i print('子 is done') if __name__ == '__main__':
print(os.cpu_count()) # 查看电脑cup内核数量:本机为4核
start_time = time.time() # 开启四个进程
p_list = []
for i in range(4):
p = Process(target=task) # 耗时3.21s
# p = Thread(target=task) 耗时5.38秒
p_list.append(p)
p.start() # 等待4个进程执行完成
for p in p_list:
p.join() end_time = time.time()
print('主done:<%s>' % (end_time-start_time))
- 如果并发的多个任务是I/O密集型:多线程效率高
# io密集型:多线程执行效率高 from multiprocessing import Process
from threading import Thread
import time
import os def task():
print('子 is running')
time.sleep(2)
print('子 is done') if __name__ == '__main__':
print(os.cpu_count()) # 查看电脑cup内核数量:本机为4核
start_time = time.time() # 开启400个进程
p_list = []
for i in range(400):
p = Process(target=task) # 耗时56s,大部分时间浪费在创建进程上
# p = Thread(target=task) # 耗时2.15秒
p_list.append(p)
p.start() for p in p_list:
p.join() end_time = time.time()
print('主done:<%s>' % (end_time-start_time))
6、死锁
from threading import Thread, Lock
import time lockA = Lock()
lockB = Lock() class Mythread(Thread): def run(self):
self.task_a()
self.task_b() def task_a(self):
lockA.acquire()
print('%s 拿到A锁1' % self.name) lockB.acquire()
print('%s 拿到B锁1' % self.name)
lockB.release() lockA.release() def task_b(self):
lockB.acquire()
print('%s 拿到B锁' % self.name) lockA.acquire()
print('%s 拿到A锁' % self.name)
lockA.release() lockB.release() if __name__ == '__main__':
for i in range(10):
t = Mythread()
t.start()
7、递归锁
#一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止
from threading import Thread, RLock
import time lockA = lockB = RLock() # 递归锁
# 递归锁可以连续acquire多次,每acquire一次计数器+1,只有计数器为0时,才能被抢到 class Mythread(Thread): def run(self):
self.task_a()
self.task_b() def task_a(self):
lockA.acquire()
print('%s 拿到A锁1' % self.name) lockB.acquire()
print('%s 拿到B锁1' % self.name)
lockB.release() lockA.release() def task_b(self):
lockB.acquire()
print('%s 拿到B锁' % self.name) lockA.acquire()
print('%s 拿到A锁' % self.name)
lockA.release() lockB.release() if __name__ == '__main__':
for i in range(10):
t = Mythread()
t.start()
8、信号量
6-[多线程]-互斥锁、GIL、死锁、递归锁、信号量的更多相关文章
- day 7-6 GIL,死锁,递归锁与信号量,Event,queue,
摘要: 1.死锁与递归锁 2.信号量 3.Event 4.Timer 5.GIL 6.Queue 7.什么时候该用多线程和多进程 一. 死锁与递归锁 所谓死锁: 是指两个或两个以上的进程或线程在执行过 ...
- python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03
目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...
- python 线程(创建2种方式,锁,死锁,递归锁,GIL锁,守护进程)
###############总结############ 线程创建的2种方式(重点) 进程:资源分配单位 线程:cpu执行单位(实体) 线程的创建和销毁的开销特别小 线程之间资源共享,是同一个 ...
- 【python】-- GIL锁、线程锁(互斥锁)、递归锁(RLock)
GIL锁 计算机有4核,代表着同一时间,可以干4个任务.如果单核cpu的话,我启动10个线程,我看上去也是并发的,因为是执行了上下文的切换,让看上去是并发的.但是单核永远肯定时串行的,它肯定是串行的, ...
- 并发、并行、同步、异步、全局解释锁GIL、同步锁Lock、死锁、递归锁、同步对象/条件、信号量、队列、生产者消费者、多进程模块、进程的调用、Process类、
并发:是指系统具有处理多个任务/动作的能力. 并行:是指系统具有同时处理多个任务/动作的能力. 并行是并发的子集. 同步:当进程执行到一个IO(等待外部数据)的时候. 异步:当进程执行到一个IO不等到 ...
- python-GIL、死锁递归锁及线程补充
一.GIL介绍 GIL全称 Global Interpreter Lock ,中文解释为全局解释器锁.它并不是Python的特性,而是在实现python的主流Cpython解释器时所引入的一个概念,G ...
- 并发编程8 线程的创建&验证线程之间数据共享&守护线程&线程进程效率对比&锁(死锁/递归锁)
1.线程理论以及线程的两种创建方法 2.线程之间是数据共享的与join方法 3.多线程和多进程的效率对比 4.数据共享的补充线程开启太快 5.线程锁 互斥锁 同步锁 6.死锁现象和递归锁 7.守护线程 ...
- day33 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁
今日内容: 1.线程理论 2.锁: 牺牲了效率,保证了数据的安全(重点) 3.守护线程 4.GIL锁:(重点) 5.计算密集型和IO密集型 6.信号量,事件(了解) 7.补充. 子进程中不能input ...
- 线程锁(互斥锁Mutex)及递归锁
一.线程锁(互斥锁) 在一个程序内,主进程可以启动很多个线程,这些线程都可以访问主进程的内存空间,在Python中虽然有了GIL,同一时间只有一个线程在运行,可是这些线程的调度都归系统,操作系统有自身 ...
- 并发编程---死锁||递归锁---信号量---Event事件---定时器
死锁 互斥锁:Lock(),互斥锁只能acquire一次 递归锁: RLock(),可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire # 死锁 f ...
随机推荐
- DPM恢复点和保持期、常见问题排除指南
“恢复点计划”确定应为该保护组创建多少个恢复点. 对于文件保护,选择希望创建恢复点的日期和时间. 对于支持增量备份的应用程序数据保护,同步频率决定了恢复点计划. 对于不支持增量备份的应用程序数据保护, ...
- 工具类-vim在shell中卡死的情况
time:2015/11/35 在xshell下面使用vim编辑,有时候会出现突然卡死的情况.但是如果重新开一个终端的话,打开文件又是一大堆问题,今天又碰到了,搜了一下就找到一个帮助了[1] 原因:按 ...
- Build path entry is missing: config 引起的 The project: configwhich is referenced by the classpath, does not exist.
运行Junit的时候报错, The project: XXXX which is referenced by the classpath, does not exist. 在Java Build Pa ...
- python字典去重脚本
#!/usr/bin/env python # encoding: utf-8 #字典去重小代码 import sys import os import platform try: pass exce ...
- 内置数据结构(list)
列表.元组.字符串.字典和集合是python内置的数据结构,也可以叫内置容器.前3个是线性结构,线性结构可以切片操作.解包和封包操作. dir()方法可以查看对象拥有哪些属性和方法. help()方法 ...
- September 23rd 2017 Week 38th Saturday
Lonely people will always remember his life occurred in each person. 寂寞的人总是会用心记住他生命中出现过的每个人. If you ...
- November 5th Week 45th Saturday 2016
The longest day has an end. 最难过的日子也会有尽头. No, no, no, I can't see the end of such days, of course, if ...
- rss工具及资源
使用的工具:reeder 资源: https://calayer.com https://github.com/SwiftOldDriver/iOS-Weekly/releases https://w ...
- log4j配置不生效
可能原因是打包时候log4j.properties没有在classpath下面或者是加载时候被其他第三方jar包中的log4j.properties文件覆盖掉了,可以使用如下命令进行查看log4j具体 ...
- 集合之HashMap
HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-value总是会当做一个整体来处理,系统会根据 ...