铁乐学python_Day42_线程-信号量事件条件

线程中的信号量

同进程的一样,Semaphore管理一个内置的计数器,

每当调用acquire()时内置计数器-1;调用release() 时内置计数器+1;

计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。

实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):
from threading import Thread,Semaphore
import threading
import time def func():
sm.acquire()
print('%s get sm' %threading.current_thread().getName())
time.sleep(3)
sm.release() if __name__ == '__main__':
sm=Semaphore(5)
for i in range(23):
t=Thread(target=func)
t.start()

与进程池是完全不同的概念,进程池Pool(4),最大只能产生4个进程,

而且从头到尾都只是这四个进程,不会产生新的,而信号量是产生一堆线程/进程。

线程中的事件

同进程的一样,线程的一个关键特性是每个线程都是独立运行且状态不可预测。

如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。

为了解决这些问题,我们需要使用threading库中的Event对象。

对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。

在初始情况下,Event对象中的信号标志被设置为假。

如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。

一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。

如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行。

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

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

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

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

例如,有多个工作线程尝试链接MySQL,要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,

如果连接不成功,都会去尝试重新连接。可以采用threading.Event机制来协调各个工作线程的连接操作。

例:模拟连接mysql数据库
import threading
import time,random
from threading import Thread,Event def conn_mysql():
count=1
while not event.is_set():
if count > 3:
raise TimeoutError('链接超时')
print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count))
event.wait(0.5)
count+=1
print('<%s>链接成功' %threading.current_thread().getName()) def check_mysql():
print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName())
time.sleep(random.randint(2,4))
event.set() if __name__ == '__main__':
event=Event()
conn1=Thread(target=conn_mysql)
conn2=Thread(target=conn_mysql)
check=Thread(target=check_mysql) conn1.start()
conn2.start()
check.start() 例:模拟连接数据库2
import time
import random
from threading import Event, Thread # 模拟连接数据库
def connect_db(e):
count = 1
while count < 4:
print('尝试第%s次检测连接' % count)
e.wait(0.5)
# 如果不传参数会一直等到事件为True为止
# 如果传参数 传一个时间参数
count += 1
if e.is_set():
print('连接成功')
break
else:
print('连接失败') def check_conn(e):
'''检测数据库是否可以连接'''
time.sleep(random.randint(1, 2))
e.set() e = Event()
Thread(target=check_conn, args=(e,)).start()
Thread(target=connect_db, args=(e,)).start() # 当你要做一件事情是有前提(前置条件)的时候
# 你就先去处理前提(前置条件)的问题 —— 前提处理好了,把状态设置成True
# 来控制即将要做的事情可以开始了。 运行效果如下:
尝试第1次检测连接
尝试第2次检测连接
连接成功

条件(Condition)

使线程等待,当满足某条件时,才释放n个线程。

Python提供的Condition对象提供了对复杂线程同步问题的支持。

Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。

线程首先acquire一个条件变量,然后判断一些条件。

如果条件不满足则wait;

如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,

其他处于wait状态的线程接到通知后会重新判断条件。

不断的重复这一过程,从而解决复杂的同步问题。

例:线程设置条件
import threading def run(n):
con.acquire()
con.wait()
print("run the thread: %s" % n)
con.release() if __name__ == '__main__': con = threading.Condition()
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start() while True:
inp = input('>>>')
if inp == 'q':
break
con.acquire()
con.notify(int(inp))
con.release()
print('****') 运行如下:
>>>1
****
>>>run the thread: 0
2
****
>>>run the thread: 1
run the thread: 2
q 例2:通过条件变量控制线程分批执行
from threading import Condition, Thread def func(i, con):
con.acquire()
con.wait()
print(i * '*')
con.release() con = Condition()
# 定义了范围从1到9
for i in range(1, 10):
Thread(target=func, args=(i, con)).start()
while True:
# 分批控制线程执行
n = int(input('>>>'))
if n == 0:
break
con.acquire()
con.notify(n)
con.release() 运行效果如下:
>>>1
>>>*
2
>>>**
***
3
>>>******
****
*****
4
>>>*******
********
*********
0 以上,输入1则运行一次任务,1+2+3+4 10次己超出任务次数,完成后不会重复执行 例:那朵花躲迷藏
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# ____ Condition 条件变量
# ____ 模拟那朵花中的面码捉迷藏对话 import threading, time # 将躲迷藏中搜寻的角色这个类创造出来,且继承了线程Thread这个类
class Seeker(threading.Thread): def __init__(self, cond, name):
super(Seeker, self).__init__()
self.cond = cond
self.name = name def run(self):
# 睡一秒,让躲猫猫的面码也运行起来,不然会阻塞住
time.sleep(1)
self.cond.acquire()
print(self.name + ': (藏)好了吗?')
self.cond.notify()
self.cond.wait()
print(self.name + ': (藏)好了吗?~~')
self.cond.notify()
self.cond.wait()
print(self.name + ': 看到你了!面码!')
self.cond.notify()
self.cond.wait()
self.cond.release()
print(self.name + ": 谢谢你,面码~ ") # 再来是躲迷藏的面码
class Hider(threading.Thread): def __init__(self, cond, name):
super(Hider, self).__init__()
self.cond = cond
self.name = name def run(self):
self.cond.acquire()
self.cond.wait()
# 释放对锁的占用,同时线程挂起来在这里,直到被notify
print(self.name + ": 还没好哦~")
self.cond.notify()
self.cond.wait()
print(self.name + ": (藏)好了哦~")
self.cond.notify()
self.cond.wait()
self.cond.notify()
self.cond.release()
print(self.name + ": 阿,被看到了~") cond = threading.Condition()
seeker = Seeker(cond, "仁太")
hider = Hider(cond, "面码")
seeker.start()
hider.start() 运行效果:交替进行的对话 仁太: (藏)好了吗?
面码: 还没好哦~
仁太: (藏)好了吗?~~
面码: (藏)好了哦~
仁太: 看到你了!面码!
面码: 阿,被看到了~
仁太: 谢谢你,面码~

无限循环的例子

经典的生产者与消费者问题:假设有一群生产者(Producer)和一群消费者(Consumer)通过一个市场来交互产品。

生产者的”策略“是如果市场上剩余的产品少于1000个,那么就生产100个产品放到市场上;

而消费者的”策略“是如果市场上剩余产品的数量多余100个,那么就消费3个产品。

用Condition解决生产者与消费者问题的代码如下:

#!/usr/bin/env python
# _*_ coding: utf-8 _*_
# ____消费者与生产者模型,多线程,条件变量,无限循环 import threading
import time # 生产者
class Producer(threading.Thread): def run(self):
global count
while True:
if con.acquire():
# 当产品在市场超过1000个时开始等候
if count > 1000:
con.wait()
# 少于1000个则开始生产100个产品投入市场
else:
count = count + 100
msg = self.name + ' produce 100, count=' + str(count)
print(msg)
con.notify()
con.release()
time.sleep(1) # 消费者
class Consumer(threading.Thread): def run(self):
global count
while True:
# 当市场少于200个产品时,消费者等候
if con.acquire():
if count < 200:
con.wait()
# 否则消费
else:
count = count - 30
msg = self.name + ' consume 30, count=' + str(count)
print(msg)
con.notify()
con.release()
time.sleep(1) # 初始产品为100个
count = 100
con = threading.Condition() def main():
# 两个生产者
for i in range(2):
p = Producer()
p.start()
# 五个消费者
for i in range(5):
c = Consumer()
c.start() if __name__ == '__main__':
main() 无限循环的部分运行效果:
Thread-1 produce 100, count=200
Thread-2 produce 100, count=300
Thread-3 consume 30, count=270
Thread-4 consume 30, count=240
Thread-5 consume 30, count=210
Thread-6 consume 30, count=180
Thread-1 produce 100, count=280
Thread-3 consume 30, count=250
Thread-5 consume 30, count=220
Thread-2 produce 100, count=320
Thread-4 consume 30, count=290
Thread-6 consume 30, count=260
Thread-7 consume 30, count=230
Thread-1 produce 100, count=330
Thread-4 consume 30, count=300
Thread-2 produce 100, count=400
Thread-5 consume 30, count=370
Thread-3 consume 30, count=340
Thread-6 consume 30, count=310
Thread-7 consume 30, count=280
Thread-1 produce 100, count=380
Thread-5 consume 30, count=350
Thread-3 consume 30, count=320
Thread-4 consume 30, count=290
Thread-2 produce 100, count=390
Thread-6 consume 30, count=360
Thread-7 consume 30, count=330
Thread-1 produce 100, count=430
Thread-3 consume 30, count=400
Thread-2 produce 100, count=500

浅析区别

  • 信号量 semaphore 允许统一时刻n个线程执行这段代码
  • 事件 event 有一个内部的事件来控制wait的行为且控制的是所有的线程
  • 条件 condition 有一个内部的条件来控制wait的行为,可以逐个或者分批次的控制线程的走向

end

参考:http://www.cnblogs.com/Eva-J/articles/8306047.html

铁乐学python_Day42_线程-信号量事件条件的更多相关文章

  1. 铁乐学python_Day42_线程池

    铁乐学python_Day42_线程池 concurrent.futures 异步调用模块 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor: ...

  2. 铁乐学python_Day42_锁和队列

    铁乐学python_Day42_锁和队列 例:多个线程抢占资源的情况 from threading import Thread import time def work(): global n tem ...

  3. Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures

    参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...

  4. python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...

  5. 铁乐学python_Day41_线程01

    线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程. 程序和进程的区别就在于: 程序是 ...

  6. 铁乐学python_Day39_多进程和multiprocess模块2

    铁乐学python_Day39_多进程和multiprocess模块2 锁 -- multiprocess.Lock (进程同步) 之前我们千方百计实现了程序的异步,让多个任务可以同时在几个进程中并发 ...

  7. 进程同步控制(锁,信号量,事件), 进程通讯(队列和管道,生产者消费者模型) 数据共享(进程池和mutiprocess.Pool模块)

    参考博客 https://www.cnblogs.com/xiao987334176/p/9025072.html#autoid-1-1-0 进程同步(multiprocess.Lock.Semaph ...

  8. Day034--Python--锁, 信号量, 事件, 队列, 生产者消费者模型, joinableQueue

    进程同步: 1. 锁 (重点)    锁通常被用来实现对共享资源的同步访问.为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁, ...

  9. python 全栈开发,Day39(进程同步控制(锁,信号量,事件),进程间通信(队列,生产者消费者模型))

    昨日内容回顾 python中启动子进程并发编程并发 :多段程序看起来是同时运行的ftp 网盘不支持并发socketserver 多进程 并发异步 两个进程 分别做不同的事情 创建新进程join :阻塞 ...

随机推荐

  1. 复刻smartbits的国产网络测试工具minismb-如何测试ip限速

    复刻smartbits的网路性能测试工具MiniSMB,是一款专门用于测试智能路由器,网络交换机的性能和稳定性的软硬件相结合的工具.可以通过此工具测试任何ip网络设备的端口吞吐率,带宽,并发连接数和最 ...

  2. 谈谈CSS的浮动问题

    浮动的工作原理 浮动元素脱离文档流,不占据空间.浮动元素碰到包含它的边框或者浮动元素的边框则停留. 浮动元素可能引起的问题 1.父元素的高度无法被撑开,影响与父级元素同级的元素 2.与浮动元素同级的非 ...

  3. C#比较两个字符串的相似度【转】

    原文地址:http://www.2cto.com/kf/201202/121170.html 我们在做数据系统的时候,经常会用到模糊搜索,但是,数据库提供的模糊搜索并不具备按照相关度进行排序的功能. ...

  4. 【JavaScript 从零开始】 原始值和对象引用、类型转换

    JavaScript 中的原始值(undefined.null . 布尔值.数值和字符串)于对象(包括数组和函数)有着根本区别. 原始值是不可更改的:任何方法都无法改变(或“突变”)一个原始值. 对于 ...

  5. Java基础教程(15)--枚举类型

      枚举类型定义了一个枚举值的列表,每个值是一个标识符.例如,下面的语句声明了一个枚举类型,用来表示星期的可能情况: public enum Day { SUNDAY, MONDAY, TUESDAY ...

  6. 后台管理UI+功能

    kingRoad功能强大,丰富各种功能和插件,自定义开发功能组件及样式,使用webpack方式集成和纯html两种,UI(自定义开发和AdminLTE的集成优化),弹窗功能使用(Layer弹窗,并与框 ...

  7. border-radius 移动之伤

    border-radius我相信对于老一辈的前端们有着特殊的感情,在经历了没有圆角的蛮荒时代,到如今 CSS3 遍地开花,我们还是很幸福的. 然而即使到了三星大脸流行时代,border-radius在 ...

  8. phoenix使用vue--单独js(不使用app.js)

    实际中不能都在一个js里 api.js app.js admin.js --vue 后台 记录下方法 static--admin--hello.js import "phoenix_html ...

  9. Portal的安全代理(反向代理出口)配置架构

    对于正式运行的环境,一般需要设置网络安全控制区DMZ,通过代理,把仅需要的端口向客户端暴露,其他内部端口应该是在防火墙包含之内的. 下文将针对ArcGIS 的Portal软件,讲述在DMZ中如何架构的 ...

  10. VMware桥接模式下主机和和虚机间互相ping不通的处理方法

    在 "编辑"->"虚拟网络编辑器" 里面的vmnet0 桥接模式 里面是自动连接,把他改为真实的物理网卡即可,如下图: