多线程的控制方式

目录

1 唤醒单个线程等待

Condition类相当于一把高级的锁,可以进行一些复杂的线程同步控制。一般Condition内部都有一把内置的锁对象(默认为RLock),对于Condition的使用主要有以下步骤:

建立两个线程对象,及Condition对象;

线程1首先获取Condition的锁权限,acquire();

线程1执行需要完成的任务后,调用等待wait(),此时,线程1会阻塞挂起,出让内置锁的控制权(即Condition可被其他线程acquire);

线程2对Condition内置锁的权限进行获取acquire(),当线程2执行需要完成的任务后,会调用唤醒,notify(),但此时线程1并不会立即被唤醒继续执行,而是要等待线程2释放Condition的内置锁release()之后,线程1才会唤醒;

线程2释放锁release()之后,线程1自动重新对锁进行占有,直到线程1完成所有任务后,再执行释放锁release(),从而结束。

1 from threading importThread, Condition2 importtime3 cond =Condition()4

5 classSleeper(Thread):6 def __init__(self):7 super(Sleeper, self).__init__()8

9 defrun(self):10 print('Sleeper gets into room, starts sleeping')11 cond.acquire()12 print('Sleeper is sleeping, waiting for wakeup')13 cond.wait()14 print('Sleeper is waked up')15 cond.release()16 print('Sleeper out of room')17

18

19 classWaker(Thread):20 def __init__(self):21 super(Waker, self).__init__()22

23 defrun(self):24 print('Waker waiting sleeper getting into room and sleeping...')25 time.sleep(1)26 cond.acquire()27 print('Waker gets into the room')28 cond.notify()29 print('Waker trying to wake up sleeper')30 time.sleep(3)31 print('Waker finished wake up, leave the room')32 cond.release()33

34 sleeper =Sleeper()35 waker =Waker()36 sleeper.start()37 waker.start()

上面的代码中,首先导入所需的模块,生成Condition的实例,之后派生两个线程模拟sleeper和waker,其中sleeper会先对Condition进行获取权限,随后进入等待,而waker会在sleeper进入等待后,获取Condition的权限,然后尝试唤醒sleeper,随后释放Condition锁,将占有权归还,sleeper收到归还的权限后,调用release进行释放。

运行得到结果

Sleeper gets into room, starts sleeping

Sleeperis sleeping, waiting forwakeup

Waker waiting sleeper getting into roomandsleeping...

Waker gets into the room

Waker trying to wake up sleeper

Waker finished wake up, leave the room

Sleeperiswaked up

Sleeper out of room

从输出的结果中可以看到,waker在notify了sleeper之后,等待了3秒,而这三秒内,sleeper并未被彻底唤醒,而是等待waker的release()之后,sleeper才被真正唤醒继续执行。

2 唤醒多个线程等待

当有多个线程进入Condition的条件等待时,可以使用两种方式进行唤醒,第一种是利用等数量的线程唤醒,第二种是利用notify_all()函数唤醒所有线程。

下面的例子对比了两种唤醒方式

1 from threading importThread, Condition2 importrandom3 importtime4 cond =Condition()5

6 classSleeper(Thread):7 def __init__(self):8 super(Sleeper, self).__init__()9 self.num = self.name[-1]10 print('Sleeper_%s ready' %self.num)11

12 defrun(self):13 print('Sleeper_%s gets into room, starts sleeping' %self.num)14 cond.acquire()15 print('Sleeper_%s is sleeping, waiting for wakeup' %self.num)16 cond.wait()17 print('Sleeper_%s is waked up' %self.num)18 cond.release()19 print('Sleeper_%s out of room' %self.num)20

21

22 classWaker(Thread):23 def __init__(self, all=False):24 super(Waker, self).__init__()25 self.all =all26 self.num = self.name[-1]27 print('Waker_%s ready' %self.num)28

29 defrun(self):30 print('Waker_%s waiting sleeper getting into room and sleeping...' %self.num)31 time.sleep(1)32 cond.acquire()33 print('Waker_%s gets into the room' %self.num)34 ifself.all:35 cond.notify_all()36 else:37 cond.notify()38 print('Waker_%s trying to wake up sleeper' %self.num)39 time.sleep(3)40 print('Waker_%s finished wake up, leave the room' %self.num)41 cond.release()42

43 if __name__ == '__main__':44 print('-------Order Wake-------')45 sleepers =[]46 wakers =[]47 for i in range(3):48 sleepers.append(Sleeper())49 for i in range(3):50 wakers.append(Waker())51

52 random.shuffle(sleepers)53 random.shuffle(wakers)54

55 for s insleepers:56 s.start()57 for s inwakers:58 s.start()59 for s insleepers:60 s.join()61

62 print('-------All Wake-------')63 sleepers =[]64 for i in range(3):65 sleepers.append(Sleeper())66 waker = Waker(all=True)67

68 random.shuffle(sleepers)69

70 for s insleepers:71 s.start()72 waker.start()

上面的代码中,在导入所需模块后,定义Sleeper和Waker类,并让他们拥有各自名字,在主函数中首先利用单个唤醒的方式去唤醒所有线程,随后再利用全部唤醒的方式唤醒所有线程,其中随机数模块用于重新排序Sleeper。

运行得到结果

-------Order Wake-------Sleeper_1 ready

Sleeper_2 ready

Sleeper_3 ready

Waker_4 ready

Waker_5 ready

Waker_6 ready

Sleeper_1 gets into room, starts sleeping

Sleeper_1is sleeping, waiting forwakeup

Sleeper_3 gets into room, starts sleeping

Sleeper_3is sleeping, waiting forwakeup

Sleeper_2 gets into room, starts sleeping

Sleeper_2is sleeping, waiting forwakeup

Waker_4 waiting sleeper getting into roomandsleeping...

Waker_6 waiting sleeper getting into roomandsleeping...

Waker_5 waiting sleeper getting into roomandsleeping...

Waker_6 gets into the room

Waker_6 trying to wake up sleeper

Waker_6 finished wake up, leave the room

Waker_5 gets into the room

Waker_5 trying to wake up sleeper

Waker_5 finished wake up, leave the room

Sleeper_1iswaked up

Sleeper_1 out of room

Waker_4 gets into the room

Waker_4 trying to wake up sleeper

Waker_4 finished wake up, leave the room

Sleeper_3iswaked up

Sleeper_3 out of room

Sleeper_2iswaked up

Sleeper_2 out of room-------All Wake-------Sleeper_7 ready

Sleeper_8 ready

Sleeper_9 ready

Waker_0 ready

Sleeper_9 gets into room, starts sleeping

Sleeper_9is sleeping, waiting forwakeup

Sleeper_7 gets into room, starts sleeping

Sleeper_7is sleeping, waiting forwakeup

Sleeper_8 gets into room, starts sleeping

Sleeper_8is sleeping, waiting forwakeup

Waker_0 waiting sleeper getting into roomandsleeping...

Waker_0 gets into the room

Waker_0 trying to wake up sleeper

Waker_0 finished wake up, leave the room

Sleeper_9iswaked up

Sleeper_9 out of room

Sleeper_7iswaked up

Sleeper_7 out of room

Sleeper_8iswaked up

Sleeper_8 out of room

View Code

从输出的结果中可以看出,

Sleeper按顺序生成,按乱序启动;

Waker的单次唤醒,首先唤醒最先进入等待的线程,而不是最先生成的,即唤醒的顺序是按照acquire后进入wait的顺序先后进行唤醒(出让锁控制权的顺序);

Waker的notify_all函数能够一次唤醒所有等待线程,顺序与单个唤醒相同。

3 条件函数等待

利用Condition类中的wait_for函数可以实现一种条件等待,当等待的函数返回值为真的时候,线程会被唤醒并继续执行。查看wait_for源码可以看出,调用wait_for函数之后,会对传入的函数进行调用,若返回结果为True,则返回True,或返回结果为False,则进入循环中,通过对时间限制的判断,最终阻塞在wait函数上,直到超时后再次获取传入函数的返回值,若依旧False,则会由于超时判断而退出循环。

下面的例子中定义了一个等待类,以及alarm函数,waiter会在启动后调用条件等待,等待函数为alarm,当alarm返回为真后,继续函数。

1 from threading importThread, Condition2 importtime3 cond =Condition()4

5 classWaiter(Thread):6 def __init__(self, alarm):7 super(Waiter, self).__init__()8 self.alarm =alarm9 print('Waiter ready')10

11 defrun(self):12 cond.acquire()13 print('Waiter waiting for alarm...')14 cond.wait_for(self.alarm, timeout=1)15 print('Waiter received alarm')16 cond.release()17 print('Waiter completed')18

19 defalarm():20 print('Sending alarm...')21 time.sleep(5)22 print('alarm sent')23 returnTrue24

25 waiter =Waiter(alarm)26 waiter.run()

运行得到结果

Waiter ready

Waiter waitingforalarm...

Sending alarm...

alarm sent

Waiter received alarm

Waiter completed

从输出的结果可以看出,waiter也会等待alarm函数结束返回结果后再继续进行

4 事件触发标志

Event与Condition类似,实质上是一个对内置Flag监测的事件标志触发类,在生成的Event实例内,会由一个内置Flag,初始状态为False,当Event类调用wait函数时,会查看内置Flag的状态,若为True则不会阻塞,若为False则调用内置Condition类的wait函数等待唤醒。

通过对源码的查看可以看出,实质上

Event的wait函数是利用Flag以及Condition的wait函数实现;

Event的set函数是利用Condition的with(acquire, release)获得控制,并将Flag置为Ture,同时notify_all();

Event的clear函数同样是利用Condition的with获得控制,然后将标志Flag置为False。

下面的例子利用了set, wait, clear函数实现了一个传球模拟。

1 from threading importThread, Event2 importtime3

4 evt =Event()5 classMate_1(Thread):6 defrun(self):7 print('Mate_1 is running')8 time.sleep(1)9 print('Mate_1 got the ball')10 time.sleep(1)11 evt.set()12 print('Mate_1 pass the ball to others')13 evt.clear()14 evt.wait()15 time.sleep(1)16 print('Mate_1 got the ball again')17 time.sleep(1)18 print('Mate_1 makes a goal')19 evt.set()20

21 classMate_2(Thread):22 defrun(self):23 print('Mate_2 is running')24 ifevt.is_set():25 evt.clear()26 evt.wait()27 time.sleep(1)28 print('Mate_2 got the ball')29 time.sleep(1)30 evt.set()31 print('Mate_2 pass the ball to others')32 time.sleep(1)33 evt.clear()34 evt.wait()35 time.sleep(1)36 print('Mate_2 hugs Mate_1 for congratulation')37 if __name__ == '__main__':38 m =Mate_1()39 n =Mate_2()40 m.start()41 n.start()

上面的代码中,首先建立事件类实例,派生两个球员子线程,球员1会在开始后1秒收到足球,控球1秒后通过evt.set()唤醒另一个线程,模拟将球传出的过程,随后清除标志进入等待。此时球员2原本处于wait状态,收到set将内置Flag重置的信号后,被唤醒,表明自己接到球,控球1秒后再set传回,自己进入等待,球员1接到信号再次接球并得分后通知球员2,球员2则在收到进球信号后表示庆祝。

运行得到结果

Mate_1 isrunning

Mate_2isrunning

Mate_1 got the ball

Mate_1passthe ball to others

Mate_2 got the ball

Mate_2passthe ball to others

Mate_1 got the ball again

Mate_1 makes a goal

Mate_2 hugs Mate_1for congratulation

结果中可以看出,以 set 方法模拟传球,使两个线程间互相配合执行。

5函数延迟启动

利用Timer类可以实现对函数的延时启动,以及在未启动前取消启动的操作

1 from threading importTimer2 from time importctime, sleep3

4 deffunc():5 print('Current time is', ctime(), 'Hello world')6

7 timex = Timer(3, func)8 print('Current time is', ctime())9 timex.start()10 timex.join()11

12 timex = Timer(3, func)13 print('Current time is', ctime())14 timex.start()15 sleep(1)16 timex.cancel()17 print('End')

运行得到结果

Current time is Thu Aug 3 16:22:19 2017Current timeis Thu Aug 3 16:22:22 2017Hello world

Current timeis Thu Aug 3 16:22:22 2017End

从结果中可以看出,延时启动3秒起到了作用,而第二次的函数则在调用前被取消了

6设置线程障碍

对于多线程来说,可以利用Barrier类实现对指定数量的线程进行阻碍,直到线程数量达到指定值后,同时释放所有的线程。

1 from threading importThread, Barrier2 importrandom3 importtime4 COUNT =05

6 defaction():7 print('ACTION')8

9 deffoo():10 globalCOUNT11 time.sleep(random.randint(1, 7))12 print('Barrier_%d waiting' %COUNT)13 COUNT += 1

14 barr.wait()15 print('Hello, world', COUNT)16

17 barr = Barrier(7, action=action, timeout=20)18

19 threads =[]20 for i in range(7):21 threads.append(Thread(target=foo))22 for t inthreads:23 t.start()24 for t inthreads:25 t.join()26

27 print('Pass barrier')

第 1-15 行,导入所需模块后,定义一个执行函数,用于越过障碍时执行,再定义一个线程函数,用于线程执行。线程执行函数会在进入后对全局变量+1,随后进入阻塞状态。

第 16 行,设置障碍上限,且线程数量与障碍上限相同。若此处线程数量大于障碍数,则跨过障碍后,多出的两个障碍会一直阻塞。可考虑使用reset函数重置计数,达到循环。

运行得到结果

Barrier_0 waiting

Barrier_1 waiting

Barrier_2 waiting

Barrier_3 waiting

Barrier_4 waiting

Barrier_5 waiting

Barrier_6 waiting

ACTION

Hello, world7Hello, world7Hello, world7Hello, world7Hello, world7Hello, world7Hello, world7Pass barrier

从输出的结果可以看到,当障碍数量达到上限的时候,会运行执行函数,随后唤醒所有的线程。

相关阅读

参考链接

《Python 核心编程 第3版》

python2个子线程等待_Python的并发并行[1] - 线程[3] - 多线程的同步控制相关推荐

  1. java 线程等待_代码分析Java中线程的等待与唤醒

    我们先来看一下实例代码: class ThreadA extends Thread{ public ThreadA(String name) { super(name); } public void ...

  2. [并发并行]_[线程模型]_[Pthread线程使用模型之一管道Pipeline]

    场景 1.经常在Windows, MacOSX 开发C多线程程序的时候, 经常需要和线程打交道, 如果开发人员的数量不多时, 同时掌握Win32和pthread线程 并不是容易的事情, 而且使用Win ...

  3. java 多线程(一 、并发并行、线程进程、如何创建线程)

    1.并发与并行 并发:指两个或多个事件在同一个时间段内发生. 并行:指连个或多个事件在同一时刻发生(同时方式). 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单核 ...

  4. c++ 等待子线程结束_python主线程与子线程的结束顺序

    对于程序来说,如果主进程在子进程还未结束时就已经退出,那么Linux内核会将子进程的父进程ID改为1(也就是init进程),当子进程结束后会由init进程来回收该子进程. 主线程退出后子线程的状态依赖 ...

  5. Python的并发并行[1] - 线程[3] - 多线程的同步控制

    多线程的控制方式 目录 唤醒单个线程等待 唤醒多个线程等待 条件函数等待 事件触发标志 函数延迟启动 设置线程障碍 1 唤醒单个线程等待 Condition类相当于一把高级的锁,可以进行一些复杂的线程 ...

  6. [并发并行]_[线程池]_[Programming With POSIX Threads的线程池实现分析1]

    场景 1.C++标准库没有提供线程池操作, 连Win32都没有集成线程池, 相比之下macOS完善多了, 至少有operations. 多线程在执行多任务时有很大优势, 比如同时管理多个设备, 多个s ...

  7. java线程本地存储_[并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32和pthread比较]...

    场景: 1.  需要统计某个线程的对象上创建的个数. 2. 当创建的堆空间需要根据线程需要创建和结束时销毁时. 3. 因为范围是线程只能看到自己的存储数据,所以不需要临界区或互斥量来维护自己的堆内存. ...

  8. 让线程等待10秒_把python程序变成多线程

    之前我们做的自动化工具,每当开始处理数据,GUI(图形界面)就会变成无响应了,用户体验很不好. 这次,我们来给工具加上多线程,让它在处理数据的同时,GUI也能进行交互. 首先,带大家简单了解下,什么是 ...

  9. python捕捉线程错误_python 主线程捕获子线程异常

    最近,在做一个项目时遇到的了一个问题,主线程无法捕获子线程中抛出的异常. 先看一个线程类的定义 ''' Created on Oct 27, 2015 @author: wujz ''' import ...

  10. python线程创建对象_Python使用面向对象方式创建线程实现12306售票系统

    目前python 提供了几种多线程实现方式 thread,threading,multithreading ,其中thread模块比较底层,而threading模块是对thread做了一些包装,可以更 ...

最新文章

  1. 详解Printjack打印机攻击
  2. 基于旋转轮廓的点云局部浮点型和二值化特征描述(RCS)
  3. 安卓开发-Activity中finish() onDestroy() 和System.exit()的区别
  4. python编程django遇到问题Passing a 3-tuple to include() is not supported.解决方案
  5. 大话数据结构:拓扑排序
  6. CF1361C. Johnny and Megan‘s Necklace(构造,欧拉回路,传递闭包)
  7. 怎么在linux中查询yum,linux - 如何使用YUM列出包的内容?
  8. python安装robotframework报错_robotframework-autoitlibrary离线安装
  9. Netty是如何解决粘包和拆包问题的
  10. 关于线程上下文切换,你知道多少?
  11. SaaS 公司如何应对 On-Call 挑战?
  12. ADT版本不同导致的一个问题
  13. idea 一键展开所有方法 一键收纳所有方法
  14. 前后端分离项目如何部署_不用Docker前后端分离项目如何快速部署
  15. JavaWeb项目分层结构
  16. 中国移动Cmpp java实现_CMPP-java 中国移动CMPP协议java开发包 - 下载 - 搜珍网
  17. Centos7基于postfix实现extmail邮件服务器
  18. 联想服务器硬盘启动设置方法,教你联想台式机bios设定硬盘启动方法
  19. 计算机缓存怎样更改,计算机的缓存大小在哪设置?
  20. 在线模拟装机大学计算机,模拟装机实验.doc

热门文章

  1. img 标签如何使图片成为圆形
  2. 租房签合同之前的注意事项
  3. 如何检查网站死链接 分享检查死链接方法
  4. python华氏温度和摄氏温度相互转换
  5. excel 中行数据向下移动一行的方法
  6. 极客DIY开源方案分享——智能家居你也可以做,何不DIY个自动窗帘升降控制系统?(纪念我的职业生涯处女作、曾获校赛一等奖作品、上古汇编语言编程)
  7. win32项目--获取、修改计算机屏幕分辨率
  8. 【07月04日】指数估值排名
  9. 压缩照片大小——PPT实现
  10. android contentprovider 生命周期,ContentProvider销毁/生命周期