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. Spring案例1出纯注解开机

    配置QueryRunner对象:注解说明 package cn.mepu.config; import org.apache.commons.dbutils.QueryRunner; import o ...

  2. (Struts2学习系列五)Struts2默认action

    当我们访问项目下一个不存在的Action的时候,页面就会报错,404找不到资源,这样对用户来说是非常不友好的,所以我们设置一个默认的Action,当找不到对应Action的时候,就会跳转到默认Acti ...

  3. vim 中 ctags的应用

    为了方便查询代码段中代码的最终的定义 在linux的vim便以其中可以使用ctags 使用步骤: 1. 安装 ctags :   sudo apt-get install ctags     2. 生 ...

  4. robotframework+python3+selenium之下拉框的选择---第五集

    由于我没有找到option形式的,所以借鉴其他大神的博客内容,如下: 1.F12后看见下拉框的源码是<option xxx> 2.如果F12后看到的下拉源码是这样的: <div xx ...

  5. MySQL日期格式化 利用Mysql的DATE_FORMAT()进行日期格式转换

    碰到一个MYSQL的问题,表logstatb中moment字段的内容是"年-月-日 时:分:秒",需要查询匹配“年月日”或“时:分:秒”即可的数据条目,这个时候就可以通过下面的SQ ...

  6. thinkphp 类的扩展

    ThinkPHP的类库主要包括公共类库和应用类库,都是基于命名空间进行定义和扩展的.只要按照规范定义,都可以实现自动加载. 大理石平台价格 公共类库 公共类库通常是指ThinkPHP/Library目 ...

  7. CF601C Kleofáš and the n-thlon(期望+前缀和优化dp)

    传送门 解题思路 要求这个人的排名,我们可以先求出某个人比他排名靠前的概率,然后再乘上\(m-1\)即为答案.求某个人比他排名靠前可以用\(dp\),设\(f[i][j]\)表示前\(i\)场比赛某人 ...

  8. NX二次开发-UFUN重命名工程图UF_DRAW_rename_drawing

    NX9+VS2012 #include <uf.h> #include <uf_draw.h> #include <uf_part.h> UF_initialize ...

  9. $nextTick与nextTick

    $nextTick Data-Dom-之后回调 nextTick Data-回调-Dom

  10. Mybatis笔记 – Po映射类型

    一.输入映射类型 parameterType定义输入到sql中的映射类型,可以是  简单类型  .po类对象(可自动生成 或 手动定义). pojo包装对象(用于综合查询,UserCustom用户自定 ...