Python并发编程之线程消息通信机制任务协调(四)
大家好,并发编程
进入第四篇。
本文目录
- 前言
- Event事件
- Condition
- Queue队列
- 总结
. 前言
前面我已经向大家介绍了,如何使用创建线程,启动线程。相信大家都会有这样一个想法,线程无非就是创建一下,然后再start()
下,实在是太简单了。
可是要知道,在真实的项目中,实际场景可要我们举的例子要复杂的多得多,不同线程的执行可能是有顺序的,或者说他们的执行是有条件的,是要受控制的。如果仅仅依靠前面学的那点浅薄的知识,是远远不够的。
那今天,我们就来探讨一下如何控制线程的触发执行。
要实现对多个线程进行控制,其实本质上就是消息通信机制在起作用,利用这个机制发送指令,告诉线程,什么时候可以执行,什么时候不可以执行,执行什么内容。
经过我的总结,线程中通信方法大致有如下三种:
- threading.Event
- threading.Condition
- queue.Queue
先抛出结论,接下来我们来一一探讨下。
. Event事件
Python提供了非常简单的通信机制 Threading.Event
,通用的条件变量。多个线程可以等待某个事件的发生
,在事件发生后,所有的线程
都会被激活
。
关于Event的使用也超级简单,就三个函数
event = threading.Event()
# 重置event,使得所有该event事件都处于待命状态
event.clear()
# 等待接收event的指令,决定是否阻塞程序执行
event.wait()
# 发送event指令,使所有设置该event事件的线程执行
event.set()
举个例子来看下。
import time
import threading
class MyThread(threading.Thread):
def __init__(self, name, event):
super().__init__()
self.name = name
self.event = event
def run(self):
print('Thread: {} start at {}'.format(self.name, time.ctime(time.time())))
# 等待event.set()后,才能往下执行
self.event.wait()
print('Thread: {} finish at {}'.format(self.name, time.ctime(time.time())))
threads = []
event = threading.Event()
# 定义五个线程
[threads.append(MyThread(str(i), event)) for i in range(1,5)]
# 重置event,使得event.wait()起到阻塞作用
event.clear()
# 启动所有线程
[t.start() for t in threads]
print('等待5s...')
time.sleep(5)
print('唤醒所有线程...')
event.set()
执行一下,看看结果
Thread: 1 start at Sun May 13 20:38:08 2018
Thread: 2 start at Sun May 13 20:38:08 2018
Thread: 3 start at Sun May 13 20:38:08 2018
Thread: 4 start at Sun May 13 20:38:08 2018
等待5s...
唤醒所有线程...
Thread: 1 finish at Sun May 13 20:38:13 2018
Thread: 4 finish at Sun May 13 20:38:13 2018
Thread: 2 finish at Sun May 13 20:38:13 2018
Thread: 3 finish at Sun May 13 20:38:13 2018
可见在所有线程都启动(start()
)后,并不会执行完,而是都在self.event.wait()
止住了,需要我们通过event.set()
来给所有线程发送执行指令才能往下执行。
. Condition
Condition和Event 是类似的,并没有多大区别。
同样,Condition也只需要掌握几个函数即可。
cond = threading.Condition()
# 类似lock.acquire()
cond.acquire()
# 类似lock.release()
cond.release()
# 等待指定触发,同时会释放对锁的获取,直到被notify才重新占有琐。
cond.wait()
# 发送指定,触发执行
cond.notify()
举个网上一个比较趣的捉迷藏的例子来看看
import threading, time
class Hider(threading.Thread):
def __init__(self, cond, name):
super(Hider, self).__init__()
self.cond = cond
self.name = name
def run(self):
time.sleep(1) #确保先运行Seeker中的方法
self.cond.acquire()
print(self.name + ': 我已经把眼睛蒙上了')
self.cond.notify()
self.cond.wait()
print(self.name + ': 我找到你了哦 ~_~')
self.cond.notify()
self.cond.release()
print(self.name + ': 我赢了')
class Seeker(threading.Thread):
def __init__(self, cond, name):
super(Seeker, self).__init__()
self.cond = cond
self.name = name
def run(self):
self.cond.acquire()
self.cond.wait()
print(self.name + ': 我已经藏好了,你快来找我吧')
self.cond.notify()
self.cond.wait()
self.cond.release()
print(self.name + ': 被你找到了,哎~~~')
cond = threading.Condition()
seeker = Seeker(cond, 'seeker')
hider = Hider(cond, 'hider')
seeker.start()
hider.start()
通过cond来通信,阻塞自己,并使对方执行。从而,达到有顺序的执行。
看下结果
hider: 我已经把眼睛蒙上了
seeker: 我已经藏好了,你快来找我吧
hider: 我找到你了 ~_~
hider: 我赢了
seeker: 被你找到了,哎~~~
. Queue队列
终于到了我们今天的主角了。
从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象,这些线程通过使用put()
和 get()
操作来向队列中添加或者删除元素。
同样,对于Queue,我们也只需要掌握几个函数即可。
from queue import Queue
# maxsize默认为0,不受限
# 一旦>0,而消息数又达到限制,q.put()也将阻塞
q = Queue(maxsize=0)
# 阻塞程序,等待队列消息。
q.get()
# 获取消息,设置超时时间
q.get(timeout=5.0)
# 发送消息
q.put()
# 等待所有的消息都被消费完
q.join()
# 以下三个方法,知道就好,代码中不要使用
# 查询当前队列的消息个数
q.qsize()
# 队列消息是否都被消费完,True/False
q.empty()
# 检测队列里消息是否已满
q.full()
函数会比之前的多一些,同时也从另一方面说明了其功能更加丰富。
我来举个老师点名的例子。
from queue import Queue
from threading import Thread
import time
class Student(Thread):
def __init__(self, name, queue):
super().__init__()
self.name = name
self.queue = queue
def run(self):
while True:
# 阻塞程序,时刻监听老师,接收消息
msg = self.queue.get()
# 一旦发现点到自己名字,就赶紧答到
if msg == self.name:
print("{}:到!".format(self.name))
class Teacher:
def __init__(self, queue):
self.queue=queue
def call(self, student_name):
print("老师:{}来了没?".format(student_name))
# 发送消息,要点谁的名
self.queue.put(student_name)
queue = Queue()
teacher = Teacher(queue=queue)
s1 = Student(name="小明", queue=queue)
s2 = Student(name="小亮", queue=queue)
s1.start()
s2.start()
print('开始点名~')
teacher.call('小明')
time.sleep(1)
teacher.call('小亮')
运行结果如下
开始点名~
老师:小明来了没?
小明:到!
老师:小亮来了没?
小亮:到!
. 总结
学习了以上三种通信方法,我们很容易就能发现Event
和 Condition
是threading模块原生提供的模块,原理简单,功能单一,它能发送 True
和 False
的指令,所以只能适用于某些简单的场景中。
而Queue
则是比较高级的模块,它可能发送任何类型的消息,包括字符串、字典等。其内部实现其实也引用了Condition
模块(譬如put
和get
函数的阻塞),正是其对Condition
进行了功能扩展,所以功能更加丰富,更能满足实际应用。
Python并发编程之线程消息通信机制任务协调(四)的更多相关文章
- python并发编程之线程/协程
python并发编程之线程/协程 part 4: 异步阻塞例子与生产者消费者模型 同步阻塞 调用函数必须等待结果\cpu没工作input sleep recv accept connect get 同 ...
- 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
本文转载自Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mult ...
- Python并发编程-进程 线程 同步锁 线程死锁和递归锁
进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...
- Python并发编程之线程中的信息隔离(五)
大家好,并发编程 进入第三篇. 上班第一天,大家应该比较忙吧.小明也是呢,所以今天的内容也很少.只要几分钟就能学完. 昨天我们说,线程与线程之间要通过消息通信来控制程序的执行. 讲完了消息通信,今天就 ...
- python 并发编程 基于线程池实现并发的套接字通信
不应该让服务端随着 并发的客户端数量增多,而无数起线程,应该用线程池,限制线程数量,控制最大并发数 io密集型程序,最大并发数是2 客户端 from socket import * client = ...
- python并发编程之线程
操作系统线程理论 参考资料:http://www.cnblogs.com/Eva-J/articles/8306047.html 线程和python 理论知识 全局解释器锁GIL Python代码的执 ...
- python并发编程之线程(一):线程&守护线程&全局解释器锁
一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.pyth ...
- Python并发编程之线程池&进程池
引用 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我 ...
- Python并发编程之线程池/进程池--concurrent.futures模块
一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/ ...
随机推荐
- 海量数据挖掘MMDS week1: Link Analysis - PageRank
http://blog.csdn.net/pipisorry/article/details/48579435 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...
- CTBS问题百科
1.浏览CTBS网站时,Service Unavailable或应用程序池自动停止的现象 解决方法: 点击"开始"-"控制面板"-"管理工具" ...
- iOS设备中垂直同步开启后的帧率计数
因为iOS设备的垂直同步总是开启的所以显得帧计数意义没啥意义. 帧计数给你一个多个帧中的平均数,现实中,你帧速率只能是60,30,20,15,12以及6fps等各个常数中的一个.所有这些值都是60的因 ...
- [面试算法题]比较二叉树异同-leetcode学习之旅(5)
问题描述 Given two binary trees, write a function to check if they are equal or not. Two binary trees ar ...
- linux下播放组播流出现setsockopt:No such device错误
在linux下播放组播流出现setsockopt:No such device错误是因为多播IP没有add路由表里面 可以采用如下命令完成: root@android:/ # busybox rout ...
- 非阻塞IO模式原理
与阻塞模式对应的另一种模式叫非阻塞IO模式,在整个通信过程中读和写操作不会阻塞,当前处理线程不存在阻塞情况.从A机器到B机器它的通信过程是:A机器一条线程将通道设置为写事件后往下执行,而另外一条线程遍 ...
- 【面试笔试算法】Program 3 : Complicated Expression(网易游戏笔试题)
时间限制:50000ms 单点时限:5000ms 内存限制:256MB 描述 在lisp语言中,表达式都用前缀法表示,例如,1 + 2 在lisp中使用(+ 1 2)来表示,其中,表达式的括号是必需的 ...
- 集群增量会话管理器——DeltaManager
DeltaManager会话管理器是tomcat默认的集群会话管理器,它主要用于集群中各个节点之间会话状态的同步维护,由于相关内容涉及到集群,可能会需要一些集群通信相关知识,如果有疑问可结合集群相关章 ...
- SharePoint 2013 页面访问,Url中间多一段"_layouts/15/start.aspx#"
问题描述: 我想访问如下页面 http://Host/_layouts/15/ManageFeatures.aspx 点击以后页面地址没有错,但是中间多了一段"_layouts/15/sta ...
- MarioTCP:一个单机可日30亿的百万并发长连接服务器
原文:http://blog.csdn.net/everlastinging/article/details/10894493 注:如果用此服务器做变长data的传输,请在业务处理函数中为input ...