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. python--reflect

    一.反射 python 中用字符串的方式操作对象的相关属性,python 中一切皆对象,都可以使用反射 用eval 有安全隐患,用 反射就很安全 1.反射对象中的属性和方法 class A: a_cl ...

  2. Ubuntu 更新国内镜像源失败

    Ubuntu 更新国内镜像源失败 首先打开系统原来的/etc/apt/sources.list 查看,原来的仓库地址是 https 还是 http 如果是http那么说明本机的 CA 证书有问题,运行 ...

  3. Ubuntu Server 19配置静态IP

    这个/etc/netplan下默认有个文件50-cloud-init.yaml,直接修改它就行了 sudo vim /etc/netplan/50-cloud-init.yaml 网口名字ens33可 ...

  4. PHP算法之电话号码的字母组合

    给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23"输出:[" ...

  5. 影响RAKsmart服务器稳定性的相关因素

    RAKsmart美国服务器近年来凭借着成熟的技术和性价比吸引着广大站长,那RAKsmart服务器稳定性怎么样呢?有什么影响因素呢?下面来了解一下吧. 因素一:服务器配置 服务器能正常运营是建立在服务器 ...

  6. Echart中X轴数据过多时横向拉动展示

    chart.setOption( { tooltip: { trigger: 'axis' }, toolbox: { feature: { saveAsImage: {} } }, grid: { ...

  7. jq-demo-点击选择(英雄联盟)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. ArrayList集合二

    集合的遍历 通过集合遍历,得到集合中每个元素,这是集合中最常见的操作.集合的遍历与数组的遍历很像,都是通过索引的方式,集合遍历方式如下 13 import java.util.ArrayList; 1 ...

  9. 反演+分块套分块——bzoj2154

    题解都在论文里了 #include<bits/stdc++.h> using namespace std; #define maxn 10000005 #define ll long lo ...

  10. Mac 精品软件

    Snagit:Mac 平台下最优秀的屏幕截图软件,可以录制屏幕视频.截图以及对截图进行加工. Flux 4:强大易用的网页设计工具,不需要学习编程即可在一天内建成一个专业的网站 Jump Deskto ...