python多线程,event,互斥锁,死锁,递归锁,信号量
Python多线程/event
多线程-threading
python的thread模块是⽐较底层的模块, python的threading
模块是对thread做了⼀些包装的, 可以更加⽅便的被使⽤
1. 使⽤threading模块
单线程执⾏
import time
def saySorry():
print("亲爱的, 我错了, 我能吃饭了吗? ")
time.sleep(1)
if __name__ == "__main__":
for i in range(5):
saySorry()
运⾏结果: 打印了五次花了五秒
亲爱的, 我错了, 我能吃饭了吗?
亲爱的, 我错了, 我能吃饭了吗?
亲爱的, 我错了, 我能吃饭了吗?
亲爱的, 我错了, 我能吃饭了吗?
亲爱的, 我错了, 我能吃饭了吗? Process finished with exit code 0
多线程执⾏ :一起打印,花了一秒
#coding=utf-8
import threading
import time
def saySorry():
print("亲爱的, 我错了, 我能吃饭了吗? ")
time.sleep(1) if __name__ == "__main__":
for i in range(5):
t = threading.Thread(target=saySorry)
t.start() #启动线程, 即让线程开始执⾏
说明
1. 可以明显看出使⽤了多线程并发的操作, 花费时间要短很多
2. 创建好的线程, 需要调⽤ start() ⽅法来启动
2. 主线程会等待所有的⼦线程结束后才结束
#coding=utf-8
import threading
from time import sleep,ctime def sing():
for i in range(3):
print("正在唱歌...%d"%i)
sleep(1) def dance():
for i in range(3):
print("正在跳舞...%d"%i)
sleep(1) if __name__ == '__main__':
print('---开始---:%s'%ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
#sleep(5) # 屏蔽此⾏代码, 试试看, 程序是否会⽴⻢结束?
print('---结束---:%s'%ctime())
3. 查看线程数量
#coding=utf-8
import threading
from time import sleep,ctime def sing():
for i in range(3):
print("正在唱歌...%d"%i)
sleep(1) def dance():
for i in range(3):
print("正在跳舞...%d"%i)
sleep(1) if __name__ == '__main__':
print('---开始---:%s'%ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start() while True:
length = len(threading.enumerate())
print('当前运⾏的线程数为: %d'%length)
if length<=1:
break
sleep(0.5)
event
flag = threading.Event() # 这个对象里面的值默认是 False
flag.wait() # 查看 flag 里面的值,如果这个值是 false 的话,就在这里等待它变成 True ,然后再执行后面的代码,括号里面可以写参数,意思为 超时执行
flag.set() # 把 flag 里面的值改为 True
flag.clear() # 把 flag 里面的值改为 False
import threading
import time
import random def foo(n):
count = 1
while not event.is_set():
print("%s:I will connect Server.....%s"%(n, count))
event.wait(2)
count += 1 print("aha i got it.....") if __name__ == "__main__":
event = threading.Event() for i in range(5):
t = threading.Thread(target=foo, args=(i+1,))
t.start() time.sleep(100) event.set()
互斥锁
我们兴许会写出这样的代码,我们假设跑100个线程,但是这100个线程都会去访问某个公共资源(比如说下面的 num 这个全局变量),
并对该资源进行处理(num -= 1)
import time
import threading num = 100 def sub():
global num tmp = num
time.sleep(0.0001)
num = tmp-1 time.sleep(2) if __name__ == "__main__":
l = []
for i in range(100):
tb = threading.Thread(target=sub)
tb.start()
l.append(tb) for tb in l:
tb.join() print(num)
但是我们看下运行结果: 72
上面的运行结果通常为小于100,大于0
这是因为我们没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。
这种现象称为“线程不安全”。在开发过程中我们必须要避免这种情况,那怎么避免?这就用到了我们在综述中提到的互斥锁了。
互斥锁概念
Python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。这个标记用来保证在任一时刻,只能有一个线程访问该对象。在Python中我们使用threading模块提供的Lock类。
我们对上面的程序进行整改,为此我们需要添加一个互斥锁变量lock = threading.Lock(),然后在争夺资源的时候之前我们会先抢占这把锁lock.acquire(),对资源使用完成之后我们在释放这把锁lock.release
def sub():
global num lock.acquire()
tmp = num
time.sleep(0.0001)
num = tmp-1
lock.release()
time.sleep(2) if __name__ == "__main__":
lock = threading.Lock() l = []
for i in range(100):
tb = threading.Thread(target=sub)
tb.start()
l.append(tb) for tb in l:
tb.join() print(num)
同步阻塞
当一个线程调用Lock对象的acquire()方法获得锁时,这把锁就进入“locked”状态。因为每次只有一个线程1可以获得锁,所以如果此时另一个线程2试图获得这个锁,该线程2就会变为阻塞状态。直到拥有锁的线程1调用锁的release()方法释放锁之后,该锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。
进一步考虑
通过对公共资源使用互斥锁,这样就简单的到达了我们的目的,但是如果我们又遇到下面的情况:
遇到锁嵌套的情况该怎么办,这个嵌套是指当我一个线程在获取临界资源时,又需要再次获取;
如果有多个公共资源,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源;
上述这两种情况会直接造成程序挂起,即死锁,下面我们会谈死锁及递归锁(可重入锁)RLock。
所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,
若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,
这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,
永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。
import time
import threading
import random lockA = threading.Lock()
lockB = threading.Lock() class MyThread(threading.Thread):
# 我这里并没有重写 __init__ 这个函数,意思为,继承父类的,反正我也没打算加些新的内容进去 def run(self):
self.foo()
self.bar() def foo(self):
global lockA
global lockB
lockA.acquire()
print("%s:I am from foo, and i have locakA"%self.name) time.sleep(random.random()) lockB.acquire()
print("%s:I am from foo, and i have locakB"%self.name) lockB.release() lockA.release() def bar(self):
global lockA
global lockB
lockB.acquire()
print("%s:I am from foo, and i have locakB" % self.name) time.sleep(random.random()) lockA.acquire()
print("%s:I am from foo, and i have locakA" % self.name) lockA.release() lockB.release() for i in range(10):
mt = MyThread()
mt.start()
代码中展示了一个线程的两个功能函数分别在获取了一个竞争资源之后再次获取另外的竞争资源,我们看运行结果:
Thread-1:I am from foo, and i have locakA
Thread-1:I am from foo, and i have locakB
Thread-1:I am from foo, and i have locakB
Thread-2:I am from foo, and i have locakA
可以看到,程序已经挂起在那儿了,这种现象我们就称之为”死锁“。
避免死锁主要方法就是:正确有序的分配资源,避免死锁算法中最有代表性的算法
或者我们可以使用一个叫做 递归锁 的东西
递归锁
RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。创建好的递归锁初始计数为0,每次被调用(acquire)的时候,它的计数就会加1, 每次释放(release)的时候,它的计数就会减1,只有它的计数为 0 的时候,才能被别的线程抢夺注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的琐。这样的话,就可以解决死锁的问题了
import threading
import time
import random RLock = threading.RLock() class MyThread(threading.Thread):
# 我这里并没有重写 __init__ 这个函数,意思为,继承父类的,反正我也没打算加些新的内容进去 def run(self):
self.foo()
self.bar() def foo(self): RLock.acquire()
print("%s:I am from foo, and i have locakA"%self.name) time.sleep(random.random()) RLock.acquire()
print("%s:I am from foo, and i have locakB"%self.name) RLock.release() RLock.release() def bar(self): RLock.acquire()
print("%s:I am from foo, and i have locakB" % self.name) time.sleep(random.random()) RLock.acquire()
print("%s:I am from foo, and i have locakA" % self.name) RLock.release() RLock.release() for i in range(10):
mt = MyThread()
mt.start()
信号量semaphore
创建方法:threading.semaphore(5)
括号里面的数字为,最大运行线程数信号量,控制着对公共资源或者临界区的访问。
信号量维护着一个计数器,指定可同时访问资源或者进入临界区的线程数。每次有一个线程获得信号量时,计数器-1。若计数器为0,其他线程就停止访问信号量,直到另一个线程释放信号量。
import threading
import time
import random def foo(n):
sl.acquire()
print("I am come from foo....%s"%n)
time.sleep(random.random())
sl.release() if __name__ == "__main__":
sl = threading.Semaphore(5)
for i in range(100):
t1 = threading.Thread(target=foo, args=(i,))
t1.start()
python多线程,event,互斥锁,死锁,递归锁,信号量的更多相关文章
- python 线程(创建2种方式,锁,死锁,递归锁,GIL锁,守护进程)
###############总结############ 线程创建的2种方式(重点) 进程:资源分配单位 线程:cpu执行单位(实体) 线程的创建和销毁的开销特别小 线程之间资源共享,是同一个 ...
- day33 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁
今日内容: 1.线程理论 2.锁: 牺牲了效率,保证了数据的安全(重点) 3.守护线程 4.GIL锁:(重点) 5.计算密集型和IO密集型 6.信号量,事件(了解) 7.补充. 子进程中不能input ...
- 并发编程8 线程的创建&验证线程之间数据共享&守护线程&线程进程效率对比&锁(死锁/递归锁)
1.线程理论以及线程的两种创建方法 2.线程之间是数据共享的与join方法 3.多线程和多进程的效率对比 4.数据共享的补充线程开启太快 5.线程锁 互斥锁 同步锁 6.死锁现象和递归锁 7.守护线程 ...
- python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03
目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...
- ReactiveSwift源码解析(十一) Atomic的代码实现以及其中的Defer延迟、Posix互斥锁、递归锁
本篇博客我们来聊一下ReactiveSwift中的原子性操作,在此内容上我们简单的聊一下Posix互斥锁以及递归锁的概念以及使用场景.然后再聊一下Atomic的代码实现.Atomic主要负责多线程下的 ...
- Python进阶(3)_进程与线程中的lock(线程中互斥锁、递归锁、信号量、Event对象、队列queue)
1.同步锁 (Lock) 当全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期.这种现象称为“线程不安全”.在开发过 ...
- day 7-6 GIL,死锁,递归锁与信号量,Event,queue,
摘要: 1.死锁与递归锁 2.信号量 3.Event 4.Timer 5.GIL 6.Queue 7.什么时候该用多线程和多进程 一. 死锁与递归锁 所谓死锁: 是指两个或两个以上的进程或线程在执行过 ...
- python并发编程之多线程2------------死锁与递归锁,信号量等
一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...
- 并发编程---死锁||递归锁---信号量---Event事件---定时器
死锁 互斥锁:Lock(),互斥锁只能acquire一次 递归锁: RLock(),可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire # 死锁 f ...
- python-GIL、死锁递归锁及线程补充
一.GIL介绍 GIL全称 Global Interpreter Lock ,中文解释为全局解释器锁.它并不是Python的特性,而是在实现python的主流Cpython解释器时所引入的一个概念,G ...
随机推荐
- vue 过滤器filter的详解
1.代码运用的地方 <!-- 在双花括号中 --> {{ date | formatDate}} <!-- 在 `v-bind` 中 --> <div v-bind:id ...
- docker Dockerfile学习---构建mongodb环境
1.创建项目目录并上传包 mkdir centos_mongodb cd centos_mongodb .tgz 2.编辑配置文件 vi mongodb.conf dbpath = /data/usr ...
- 【颓废篇】Py:从零开始的poj自动提交
之前学习了一些python的爬虫技术... 已经可以通过python来水blog的阅读量了 你知道的太多了, 然而你看我这个blog惨不忍睹的访问量, 有人吗? 有人吗? 今天突然又双叒叕心血来潮想写 ...
- 一个简易h5涉及的ps技巧
事实证明,很长时间不做,是会忘掉的呀,的呀,呀,啊~ 1.合并图层 CTRL+E合并多个图层 2.切片 3.导出 文件-------导出------存储为web所用格式-------->> ...
- Java中的反射该如何使用?
1. 什么是反射 反射是一种功能强大且复杂的机制.Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性.对于任何一个对象,我们都能够对它的方法和属性进行调用.我们把这种 ...
- The past is just a story we tell ourselves.
The past is just a story we tell ourselves.过去是我们说给自己听的故事.
- JUC 一 FutureTask
java.util.concurrent public class FutureTask<V> implements RunnableFuture<V> 简介 FutureTa ...
- Linkedlist 详解
基本介绍 Linkedlist基于链表的动态数组(双向链表): 可以被当作堆栈(后进先出).队列(先进先出)或双端队列进行操作. 数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低, ...
- NX二次开发-通过点击按钮来控制显示工具条
NX9+VS2012 1.打开D:\Program Files\Siemens\NX 9.0\UGII\menus\ug_main.men 找到装配和PMI,在中间加上一段 TOGGLE_BUTTON ...
- slot 的简单用法
注:默认在父组件调用子组件时<SlotChild></SlotChild>中文字不会显示.但是在子组件加入<slot></slot>后,<Slot ...