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,互斥锁,死锁,递归锁,信号量的更多相关文章

  1. python 线程(创建2种方式,锁,死锁,递归锁,GIL锁,守护进程)

    ###############总结############ 线程创建的2种方式(重点) 进程:资源分配单位    线程:cpu执行单位(实体) 线程的创建和销毁的开销特别小 线程之间资源共享,是同一个 ...

  2. day33 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁

    今日内容: 1.线程理论 2.锁: 牺牲了效率,保证了数据的安全(重点) 3.守护线程 4.GIL锁:(重点) 5.计算密集型和IO密集型 6.信号量,事件(了解) 7.补充. 子进程中不能input ...

  3. 并发编程8 线程的创建&验证线程之间数据共享&守护线程&线程进程效率对比&锁(死锁/递归锁)

    1.线程理论以及线程的两种创建方法 2.线程之间是数据共享的与join方法 3.多线程和多进程的效率对比 4.数据共享的补充线程开启太快 5.线程锁 互斥锁 同步锁 6.死锁现象和递归锁 7.守护线程 ...

  4. python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03

    目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...

  5. ReactiveSwift源码解析(十一) Atomic的代码实现以及其中的Defer延迟、Posix互斥锁、递归锁

    本篇博客我们来聊一下ReactiveSwift中的原子性操作,在此内容上我们简单的聊一下Posix互斥锁以及递归锁的概念以及使用场景.然后再聊一下Atomic的代码实现.Atomic主要负责多线程下的 ...

  6. Python进阶(3)_进程与线程中的lock(线程中互斥锁、递归锁、信号量、Event对象、队列queue)

    1.同步锁 (Lock) 当全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期.这种现象称为“线程不安全”.在开发过 ...

  7. day 7-6 GIL,死锁,递归锁与信号量,Event,queue,

    摘要: 1.死锁与递归锁 2.信号量 3.Event 4.Timer 5.GIL 6.Queue 7.什么时候该用多线程和多进程 一. 死锁与递归锁 所谓死锁: 是指两个或两个以上的进程或线程在执行过 ...

  8. python并发编程之多线程2------------死锁与递归锁,信号量等

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

  9. 并发编程---死锁||递归锁---信号量---Event事件---定时器

    死锁 互斥锁:Lock(),互斥锁只能acquire一次 递归锁:  RLock(),可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire # 死锁 f ...

  10. python-GIL、死锁递归锁及线程补充

    一.GIL介绍 GIL全称 Global Interpreter Lock ,中文解释为全局解释器锁.它并不是Python的特性,而是在实现python的主流Cpython解释器时所引入的一个概念,G ...

随机推荐

  1. 个人使用Viso绘制的简单神经网络实现原理图

  2. 【模板篇】Link Cut Tree模板(指针)

    网上一片一片的LCT都是数组写的 orz 用指针写splay的人想用指针写LCT找板子都不好找QAQ 所以能A题了之后自然要来回报社会, 把自己的板子丢上来(然而根本没有人会看) LCT讲解就省省吧, ...

  3. javascript 跨域问题 jsonp

    转载:http://www.cnblogs.com/choon/p/5393682.html demo 用动态创建<script></script>节点的方式实现了跨域HTTP ...

  4. IDEA中统计项目代码的总行数

    方法    安装“Statistic”插件步骤    打开idea设置界面,选择 plugins标签   点击下面“Browse repositories”按扭,浏览插件仓库   搜索“Statist ...

  5. 关于canvas绘制图像模糊问题

    前段时间在做项目的裁剪并上传图像功能的时候,发现裁剪后展示的图像比较模糊,之后去百度上搜索了一下,看到有一个解决方案是设置canvas的宽高为css宽高的3倍,使用后感觉效果很好,当时就没管原理接着做 ...

  6. delphi xe10 网络文件传送

    //网络传送文件(类似Server/Client) TTetheringManager|设备管理.TTetheringAppProfile|文件发送 待补充

  7. Ruby 安装 – Windows

    Ruby 安装 – Windows 下面列出了在 Windows 机器上安装 Ruby 的步骤. 注意:在安装时,您可能有不同的可用版本. - Window 系统下,我们可以使用 RubyInstal ...

  8. 取对数求阶乘位数——lightoj1045

    /* 求 n! 在base进制下的位数 取对数,用换底公式,预处理对数前缀和 b^x = n! x = log_b(n!) = log_10(n!)/log_10(b) 对x向上取整即可 */ #in ...

  9. NX二次开发-创建图纸尺寸表达式抑制UF_DRF_add_controlling_exp

    #include <uf.h> #include <uf_modl.h> #include <uf_drf.h> #include <uf_obj.h> ...

  10. Api:目录

    ylbtech-Api:目录 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   作者:ylbtech出处:http://ylbtech.c ...