线程同步条件、信号量及队列

同步条件(event)

下面是官方文档对event的一些解释:

An event is a simple synchronization object;

the event represents an internal flag, and threads
can wait for the flag to be set, or set or clear the flag themselves.

event = threading.Event()

# a client thread can wait for the flag to be set
event.wait()

# a server thread can set or reset it
event.set()
event.clear()
If the flag is set, the wait method doesn’t do anything.
If the flag is cleared, wait will block until it becomes set again.
Any number of threads may wait for the same event

也就是说:

event.isSet():返回event的状态值;

event.wait():如果 event.isSet()==False将阻塞线程;

event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;

event.clear():恢复event的状态值为False。

通过Event,我们可以实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红绿灯的规则。

import random
def light():
if not event.isSet():
event.set() #wait就不阻塞 #绿灯状态
count = 0
while True:
if count < 10: #绿灯10s
print('\033[42;1m--green light on---\033[0m')
elif count <13: #黄灯3s
print('\033[43;1m--yellow light on---\033[0m')
elif count <20: #红灯7s
if event.isSet():
event.clear()
print('\033[41;1m--red light on---\033[0m')
else: #重新开始第二轮红绿灯
count = 0
event.set() #打开绿灯
time.sleep(1)
count +=1
def car(n):
while 1:
time.sleep(random.randrange(10))
if event.isSet(): #绿灯
print("car [%s]正常行驶" % n)
else:
print("car [%s]在等红绿灯..." %n)
if __name__ == '__main__':
event = threading.Event()
Light = threading.Thread(target=light)
Light.start()
for i in range(4):
t = threading.Thread(target=car,args=(i,))
t.start()

运行之后就会看到4辆车在红绿灯时的状态了,非常简单,再看一个小例子就行了

import threading,time
class Mom(threading.Thread): def run(self):
print("妈妈:孩子们快回来吃饭")
# print(event.isSet())# False
event.set()
time.sleep(5)
print("妈妈:吃红烧肉")
print(event.isSet())
event.set() class Son(threading.Thread):
def run(self): event.wait()# 一旦event被设定,等同于pass print("一个孩子问:吃什么啊")
time.sleep(1)
event.clear()
event.wait()
print("一个孩子说:OhYeah!") if __name__=="__main__":
event=threading.Event() threads=[]
for i in range(5):
threads.append(Son())
threads.append(Mom())
for t in threads:
t.start()
for t in threads:
t.join() print("ending.....")

信号量(Semaphore)

之前讲的同步锁,是只允许一个线程去操作数据,而Semaphore就是同时允许一定数量的线程去操作数据。怎么说呢,比如100个人去洗脚城洗脚,只有五个技师,那一次只能同时五个人洗脚,后面的就只能排队等着了。原理也比较简单,就是BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。

BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

import threading,time

class myThread(threading.Thread):
def run(self): if semaphore.acquire():
print(self.name)
time.sleep(3)
semaphore.release() if __name__=="__main__":
semaphore=threading.Semaphore(5) thrs=[]
for i in range(100):
thrs.append(myThread())
for t in thrs:
t.start()

运行后,也可能出现我们同步锁里面说的并排打印的问题,这个就要看你个人需求了。

线程队列(queue)

之前我们在学校学过什么堆栈、队列的知识,在这里,没有堆栈这个概念,只有队列。

创建一个“队列”对象
import Queue
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。 将一个值放入队列中
q.put(10)
调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为
1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。 将一个值从队列中取出
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。
如果队列为空且block为False,队列将引发Empty异常。 Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize) 此包中的常用方法(q = Queue.Queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作

现在有一个需求,比如我有一个列表,要用多线程去移除列表最后一个元素,我们一般是这么写:

import threading,time

li=[1,2,3,4]

def pri():
while li:
a=li[-1]
print(a)
time.sleep(1)
li.remove(a)
# try:
# li.remove(a)
# except Exception as e:
# print('----',a,e) t1=threading.Thread(target=pri,args=())
t1.start()
t2=threading.Thread(target=pri,args=())
t2.start()
t1.join()
t2.join()
print(li)

但是运行过程中我们会发现报错,ValueError: list.remove(x): x not in list,同时操作这个元素的时候,发现这个元素已经被其他线程移除了,所以报错。这里想说明的是,列表在多线程中的操作是不安全的。

下面这个例子,是通过queue,来实现一个生产者和消费者模型,也就是说边做边吃

import time,random
import queue,threading
q = queue.Queue()
def Producer(name):
count = 0
while count <20:
time.sleep(random.randrange(5))
q.put(count)
print('Producer %s has produced %s baozi..' %(name, count))
count +=1
def Consumer(name):
count = 0
while count <20:
time.sleep(random.randrange(4))
if not q.empty():
data = q.get()
print(data)
print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
else:
print("-----no baozi anymore----")
count +=1
p1 = threading.Thread(target=Producer, args=('A',))
c1 = threading.Thread(target=Consumer, args=('B',))
c2 = threading.Thread(target=Consumer, args=('C',))
p1.start()
c1.start()
c2.start()

A是生产者,一直在做包子,做20个,B和C就是消费者,就是一直在吃包子,没有包子就喊。这里我们用的是q.empty()来判断队列中有没有包子,我们也可以通过q.task_done()和q.join()来完成这件事:

import time,random
import queue,threading q = queue.Queue() def Producer(name):
count = 0
while count <10:
print("making........")
time.sleep(3)
q.put(count)
print('Producer %s has produced %s baozi..' %(name, count))
count +=1
q.task_done()
# q.join()
print("ok......") def Consumer(name):
count = 0
while count <10:
time.sleep(random.randrange(4))
# if not q.empty():
print("waiting.....")
q.join()
data = q.get()
print("eating....")
time.sleep(4) # q.task_done()
print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
# else:
# print("-----no baozi anymore----")
count +=1 p1 = threading.Thread(target=Producer, args=('A君',))
c1 = threading.Thread(target=Consumer, args=('B君',))
c2 = threading.Thread(target=Consumer, args=('C君',))
c3 = threading.Thread(target=Consumer, args=('D君',)) p1.start()
c1.start()
c2.start()
c3.start()

就是说A君做好包子了,告诉队列,顾客这边在jion状态,包子没做好就一直等着,知道队列收到了包子做好的消息,也会告诉顾客能吃包子了,所以这里q.task_done()和q.join()是成对出现的,单个使用没什么意义,你也可以试试在厨师和顾客中调换位置使用。

线程的知识就讲到这里了,下面要说的是进程的内容,如果前面线程都掌握了,进程就相当简单,大同小异了。

python进程和线程(四)的更多相关文章

  1. python进程、线程、协程(转载)

    python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资 ...

  2. Python进程、线程、协程详解

    进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...

  3. python进阶:Python进程、线程、队列、生产者/消费者模式、协程

    一.进程和线程的基本理解 1.进程 程序是由指令和数据组成的,编译为二进制格式后在硬盘存储,程序启动的过程是将二进制数据加载进内存,这个启动了的程序就称作进程(可简单理解为进行中的程序).例如打开一个 ...

  4. python 进程和线程(代码知识部分)

    二.代码知识部分 一 multiprocessing模块介绍: python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情 ...

  5. python 进程与线程(理论部分)

    一.理论部分 一 什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 举例(单核+多道,实现多个进程的并发执行): egon在一个时间段内有很多任务要做:python备课的 ...

  6. Python 进程、线程、协程的介绍与使用

    一.必备的理论基础 二.操作系统发展史 三.进程理论 四.线程理论 五.协程 一.必备的理论基础 操作系统理论: 操作系统是一个协调\管理\控制计算机硬件资源与应用软件资源的控制程序 操作系统的两大功 ...

  7. 这篇文章揭开python进程、线程、协程神秘的面纱

    1.概念 [关注公众号"轻松学编程"了解更多. 回复"协程"获取本文源代码.] 从计算机硬件角度: 计算机的核心是CPU,承担了所有的计算任务. 一个CPU,在 ...

  8. python 进程和线程

    python中的进程.线程(threading.multiprocessing.Queue.subprocess) Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就 ...

  9. Python进程、线程、协程

    进程和线程的解释 进程(process)和线程(thread)是操作系统的基本概念,计算机的核心是CPU,它承担了所有的计算任务: 单个CPU一次只能运行一个任务,代表单个CPU总是运行一个进程,其他 ...

  10. Python进程和线程

    引入进程和线程的概念及区别 1.线程的基本概念 概念 线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但 ...

随机推荐

  1. nodejs中处理回调函数的异常

    如果是使用nodejs+express3这个经典的组合,那么有一种很方面的处理回调函数异常的方法: 1. 安装模块:express-domain-middleware 2. 加入如下的代码: app. ...

  2. Python爬虫三年没入门,传授一下绝世神功,经理唏嘘不已!

    长期枯燥的生活,敲代码的时间三天两头往吸烟室跑,被项目经理抓去训话. "入门"是学习Python最重要的阶段,虽然这个过程也许会非常缓慢.当你心里有一个目标时,那么你学习起来就不会 ...

  3. fiddler抓包软件的使用--请求头--ajax

    User-Agent: FiddlerHost: localhost:49828Content-Length: 0Accept: application/xmlContent-Type: applic ...

  4. C++11中map的用法

    最全的c++map的用法 1. map最基本的构造函数:map<string ,int>mapstring; map<int,string >mapint;map<sri ...

  5. poj3352添加多少条边可成为双向连通图

    Road Construction Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 13311   Accepted: 671 ...

  6. Nginx高并发优化方案

    原网址: https://blog.csdn.net/HoeWang/article/details/81221463 一.一般来说nginx 配置文件中对优化比较有作用的为以下几项: 1. work ...

  7. ASP.NET Core Web API 索引 (更新Identity Server 4 视频教程)

    GraphQL 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(上) 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(下) [视频] 使用ASP.NET C ...

  8. 基于ko-easyui实现的PC前端模板功能完善

    上一篇博客我向大家介绍了基于ko-easyui实现的开发模板,博客地址:https://www.cnblogs.com/cqhaibin/p/9825465.html#4095185.但在还遗留三个问 ...

  9. 带着新人看java虚拟机04(多线程篇)

    我记得最开始接触多进程,多线程这一块的时候我不是怎么理解,为什么要有多线程啊?多线程到底是个什么鬼啊?我一个程序好好的就可以运行为什么要用到多线程啊?反正我是十分费解,即使过了很长时间我还是不是很懂, ...

  10. SpringCloud-分布式链路跟踪配置详解

    SpringCloud-分布式链路跟踪 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 注:作者使用IDEA + Gradle 注:需要有一定的java SpringBoot and ...