大家好,并发编程进入第四篇。

本文目录

前言

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并发编程之线程消息通信机制/任务协调(四)相关推荐

  1. python线程池模块_Python并发编程之线程池/进程池--concurrent.futures模块

    一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/ ...

  2. python 消息机制_Python并发编程之线程消息通信机制任务协调(四)

    . 前言 前面我已经向大家介绍了,如何使用创建线程,启动线程.相信大家都会有这样一个想法,线程无非就是创建一下,然后再start()下,实在是太简单了. 可是要知道,在真实的项目中,实际场景可要我们举 ...

  3. python互斥锁原理_python并发编程之多进程1------互斥锁与进程间的通信

    一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...

  4. python线程池模块_python并发编程之进程池,线程池,协程(Python标准模块--concurrent.futures(并发未来))...

    需要注意一下 不能无限的开进程,不能无限的开线程 最常用的就是开进程池,开线程池.其中回调函数非常重要 回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去 ...

  5. python3 线程隔离_Python并发编程之线程中的信息隔离(五)

    大家好,并发编程 进入第三篇. 上班第一天,大家应该比较忙吧.小明也是呢,所以今天的内容也很少.只要几分钟就能学完. 昨天我们说,线程与线程之间要通过消息通信来控制程序的执行. 讲完了消息通信,今天就 ...

  6. python创建新进程_Python并发编程(进程的创建)

    动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的. 并发性:任何进程都可以同其他进程一起并发执行 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的 ...

  7. python 异步io框架_Python并发编程之学习异步IO框架:asyncio 中篇(十)

    大家好,并发编程 进入第十章. 好了,今天的内容其实还挺多的,我准备了三天,到今天才整理完毕.希望大家看完,有所收获的,能给小明一个赞.这就是对小明最大的鼓励了. 为了更好地衔接这一节,我们先来回顾一 ...

  8. python线程池模块_python并发编程之进程池,线程池,协程

    需要注意一下 不能无限的开进程,不能无限的开线程 最常用的就是开进程池,开线程池.其中回调函数非常重要 回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去 ...

  9. python多线程执行其他模块的文件_python并发编程--进程线程--其他模块-从菜鸟到老鸟(三)...

    concurrent模块 1.concurrent模块的介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 Proc ...

最新文章

  1. 城市需要建什么样的能源数据中心?
  2. 【论文解读】这篇顶会paper,讲述了疫情期间憋疯的你和我
  3. java 10zhuan8,Java代码 10进制转2、8、16进制转换 / 2、8、16进制转10进制转换
  4. 今日arXiv精选 | 35篇顶会论文:ICCV/ CIKM/ ACM MM
  5. Axiso解决跨域访问 !!!!
  6. jqGrid('setSelection',rowid)报Cannot read property 'multiple' of undefined
  7. esp mounter pro_对比 | 以大欺小?剑指宋Pro和哈弗H6,欧尚X7的黑马潜质从何而来?...
  8. java 字符串加密解密_Java加密解密字符串
  9. java面试之String的理解(自我理解)
  10. ios mailto:// 用邮箱发邮件_投简历用什么邮箱最好?投简历怎么发邮件?
  11. unity shader 流光(1)
  12. 【Mybatis学习路线】day02mybatis的增删改查操作
  13. 人脸识别技术在智慧城城市建设中的深度应用
  14. 部署Kubernetes集群+Dashboard可视化页面-1.18.6版本
  15. [异常检测]Learning Regularity in Skeleton Trajectories for Anomaly Detection in Videos
  16. 曙光服务器如何重新设置u盘启动_在中科曙光I620-G20服务器上安装Windows 2008 R2 系统步骤...
  17. 内存优化 · 基础论 · 初识 Android 内存优化
  18. 什么是SEO?搜索引擎优化是什么意思?
  19. html、js、css3制作一款辉光管时钟
  20. axios访问后台404_使用 axios 后台无法接收到数据的解决方案

热门文章

  1. 标准数据库事务完整性约束
  2. C语言和C++语言在语法上面的部分区别
  3. Google真相:决策贡献及其艰苦抉择
  4. 我写过最长的东西可能就是高考作文了
  5. 标准炮灰三人组飞鸽传书武侠片
  6. Hongjin2 软件研发作为一项工程而言
  7. Python 为了提升性能,竟运用了共享经济!赶紧看看!!
  8. LeCun自曝使用C语言23年之久,2年前才用Python,还曾短暂尝试Lua
  9. 性能优化篇(3):避免空的图片src
  10. HTML5 API详解(4):最实用的API DeviceOrientation设备传感器