一、事件Event

Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果Flag值为 False,那么当程序执行 event.wait方法时就会阻塞,如果Flag值为True,那么event.wait 方法时便不再阻塞。

Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

Event()

  • set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。

  • clear(): 将标志设为False。

  • wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。

  • isSet(): 获取内置标志状态,返回True或False。

线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。

执行过程:在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行。

例子

使用场景:

有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作

from threading import Event,Thread
import time
import random


def connect_db(e):
time.sleep(random.randint(0,3))
e.set()

def check_web(e):
count = 1
time.sleep(1)
while count < 4:
e.wait(0.5) # 状态为False的时候,只等待0.5s就结束
if e.is_set():
print("第%s次链接数据库成功!"%count)
break
else:
print("第%s次链接数据库失败!"%count)
count += 1
else:
raise TimeoutError("数据库链接超时")

e = Event()
t1 = Thread(target=check_web,args=(e,))
t2 = Thread(target=connect_db,args=(e,))
t1.start()
t2.start()

  

二、 条件Condition

可以把Condition理解为一把高级的琐,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。threadiong.Condition在内部维护一个琐对象(默认是RLock),可以在创建Condigtion对象的时候把琐对象作为参数传入。

Condition也提供了acquire, release方法,其含义与琐的acquire, release方法一致,其实它只是简单的调用内部琐对象的对应的方法而已。

Condition还提供了如下方法(特别要注意:这些方法只有在占用琐(acquire)之后才能调用,否则将会报RuntimeError异常。):

  • Condition.wait([timeout]):

    wait方法释放内部所占用的琐,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话)。当线程被唤醒并重新占有琐的时候,程序才会继续执行下去。

  • Condition.notify():

    唤醒一个挂起的线程(如果存在挂起的线程)。注意:notify()方法不会释放所占用的琐。

  • Condition.notify_all() Condition.notifyAll()

    唤醒所有挂起的线程(如果存在挂起的线程)。注意:这些方法不会释放所占用的琐。

例子


from threading import Thread,Condition

def func(con,i):
con.acquire()
con.wait()
print("在执行第%s个线程"%i)
con.release()


con = Condition()
for i in range(10):
Thread(target=func,args=(con,i)).start()
while True:
num = input(">>>").strip()
if num.isdigit():
num = int(num)
else:break
con.acquire()
con.notify(num) #通知有几个线程可以执行
con.release()

# Python提供的Condition对象提供了对复杂线程同步问题的支持。
# Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,
# 还提供了wait和notify方法。线程首先acquire一个条件变量,然后判断一些条件。
# 如果条件不满足则wait;如果条件满足,进行一些处理改变条件后,
# 通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。
# 不断的重复这一过程,从而解决复杂的同步问题。

  

分析:在程序运行过程中输入数字,输入多少个数字就唤醒多少个线程执行for循环里的线程。

三、定时器Timer

Timer:隔一定时间调用一个函数,如果想实现每隔一段时间就调用一个函数的话,就要在Timer调用的函数中,再次设置Timer。Timer是Thread的一个派生类.

Timer继承子Thread类,是Thread的子类,也是线程类,具有线程的能力和特征。这个类用来定义多久执行一个函数。

它的实例是能够延迟执行目标函数的线程,在真正执行目标函数之前,都可以cancel它。

Timer()

interval 第一个参数传 间隔时间

function 传执行任务的函数 隔了多少秒后执行这个函数

给函数传参方式 args kwargs

Timer用的是Thread模块,每启动一个定时器,启动一个线程

例子


from threading import Timer
import time


def add(x, y):
print(x + y)


t = Timer(10,add,args=[4,5,])
t.start()

time.sleep(2)
t.cancel()
print("===end===")

  

 分析:start方法执行之后,Timer对象会处于等待状态,等待10秒之后会执行add函数。同时,在执行add函数之前的等待阶段,主线程使用了子线程的cancel方法,就会跳过执行函数结束。

例子

# 定时器,指定n秒后执行某个操作
from threading import Timer
import time

def func():
print("执行时间同步")


while True:
Timer(5,func).start() #注意这个线程是异步的,先执行这句,然后马上就执行time.sleep(),
# timer在等待5秒后执行func,而time.sleep()此时也等待了5秒,再次执行timer()
time.sleep(5)

  

 

四、线程queue

queue 模块下提供了几个阻塞队列,这些队列主要用于实现线程通信。在 queue 模块下主要提供了三个类,分别代表三种队列,它们的主要区别就在于进队列、出队列的不同。

关于这三个队列类的简单介绍如下:

  • Queue.qsize():返回队列的实际大小,也就是该队列中包含几个元素。

  • Queue.empty():判断队列是否为空。

  • Queue.full():判断队列是否已满。

  • Queue.put(item, block=True, timeout=None):向队列中放入元素。如果队列己满,且 block 参数为 True(阻塞),当前线程被阻塞,timeout 指定阻塞时间,如果将 timeout 设置为 None,则代表一直阻塞,直到该队列的元素被消费;如果队列己满,且 block 参数为 False(不阻塞),则直接引发 queue.FULL 异常。

  • Queue.put_nowait(item):向队列中放入元素,不阻塞。相当于在上一个方法中将 block 参数设置为 False。

  • Queue.get(item, block=True, timeout=None):从队列中取出元素(消费元素)。如果队列已满,且 block 参数为 True(阻塞),当前线程被阻塞,timeout 指定阻塞时间,如果将 timeout 设置为 None,则代表一直阻塞,直到有元素被放入队列中; 如果队列己空,且 block 参数为 False(不阻塞),则直接引发 queue.EMPTY 异常。

  • Queue.get_nowait(item):从队列中取出元素,不阻塞。相当于在上一个方法中将 block 参数设置为 False。

  • Queue.Queue(maxsize=0) #FIFO, 用来定义队列的长度,如果maxsize小于1就表示队列长度无限,

  • Queue.LifoQueue(maxsize=0) #LIFO, 如果maxsize小于1就表示队列长度无限

  • task_done()#意味着之前入队的一个任务已经完成。由队列的消费者线程调用。每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。

  • join()#阻塞调用线程,直到队列中的所有任务被处理掉。只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done((意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。

三种不同的队列类

q=Queue(maxsize):创建一个FIFO(first-in first-out,先进先出)队列。maxsize是队列中金额以放入的项的最大数量。
如果省略maxsize参数或将它置为0,队列大小将无穷大。

q=LifoQueue(maxsize):创建一个LIFO(last-in first-out,后进先出)队列(栈)。

q=PriorityQueue(maxsize):创建一个优先级队列,其中项按照优先级从低到高依次排好。
使用这种队列时,项应该是(priority,data)形式的元组,其中priority时一个标识优先级的数字。

例子

# 进程中使用队列 from multiprocessing import Queue
import queue
#
# q1 = queue.Queue() #先进先出
# q1.get_nowait() #不阻塞直接取,如果取时是空则报错
# q1.put_nowait(2) #不阻塞直接存,如果存时是满的则报错
# q1.get()
# q1.put(1)


q2 = queue.LifoQueue() #其实是一个栈,先进后出
q2.put("a")
q2.put("b")
q2.put("c")
print(q2.get())

q3 = queue.PriorityQueue() #优先级队列,根据设置的优先级确定取出的对象
q3.get((20,"a"))
q3.get((10,"c"))
q3.get((0,"b"))
q3.get((-10,"d")) #数字越小越优先取出来
q3.get((30,"e"))
print(q3.get())

  

生产者消费者模型

import threading
import time
from queue import Queue


def put_id():
i = 0
while True:
i = i + 1
print("添加数据", i, id_queue.qsize())
time.sleep(1)
id_queue.put(i)


def get_id(m):
while True:
i = id_queue.get()
print("线程", m, '取值', i)


if __name__ == "__main__":
id_queue = Queue(10)

Th1 = threading.Thread(target=put_id, )
Th2 = threading.Thread(target=get_id, args=(2,))
Th3 = threading.Thread(target=get_id, args=(3,))
Th5 = threading.Thread(target=get_id, args=(4,))
Th4 = threading.Thread(target=get_id, args=(5,))

Th1.start()
Th2.start()
Th3.start()
Th4.start()
Th5.start()

  

Python之路(第四十五篇)线程Event事件、 条件Condition、定时器Timer、线程queue的更多相关文章

  1. Python之路【第十五篇】:Web框架

    Python之路[第十五篇]:Web框架   Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 1 2 3 4 5 6 ...

  2. Python之路(第四十六篇)多种方法实现python线程池(threadpool模块\multiprocessing.dummy模块\concurrent.futures模块)

    一.线程池 很久(python2.6)之前python没有官方的线程池模块,只有第三方的threadpool模块, 之后再python2.6加入了multiprocessing.dummy 作为可以使 ...

  3. Python之路(第四十二篇)线程相关的其他方法、join()、Thread类的start()和run()方法的区别、守护线程

    一.线程相关的其他方法 Thread实例对象的方法 # isAlive(): 返回线程是否活动的. # getName(): 返回线程名. # setName(): 设置线程名. ​ threadin ...

  4. Python之路【第十五篇】WEB框架

    WEB框架本质 Python的WEB框架分为两类: 1.自己写socket,自己处理请求 2.基于wsgi(Web Server Gateway Interface WEB服务网关接口),自己处理请求 ...

  5. Python之路,第十五篇:Python入门与基础15

    python3   异常 异常(基础) 什么是错误? 错误是指由于逻辑或语法错误等,导致一个程序已无法正常执行的问题. 什么是异常? 异常是程序出错时标识的一种状态,当异常发生时,程序不会再向下执行, ...

  6. Python之路【第二十五篇】:数据库之pymysql模块

    数据库进阶 一.pymysql模块 pymysql是Python中操作Mysql的模块,其使用的方法和py2的MySQLdb几乎相同. 二.pymysql模块安装 pip install pymysq ...

  7. Python之路(第三十五篇) 并发编程:操作系统的发展史、操作系统的作用

    一.操作系统发展史 第一阶段:手工操作 —— 真空管和穿孔卡片 ​ 第一代之前人类是想用机械取代人力,第一代计算机的产生是计算机由机械时代进入电子时代的标志,从Babbage失败之后一直到第二次世界大 ...

  8. 【Python之路】第十五篇--Web框架

    Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf- ...

  9. Python之路【第十五篇】开发FTP多线程程序

    要求: 1.用户加密认证 2.允许同时多用户登录 3.每个用户有自己的家目录,且只能访问自己的家目录 4.对用户进行磁盘配额,每个用户的可用空间不同 5.允许用户在ftp server上随意切换目录 ...

随机推荐

  1. 【Spring AOP】AOP介绍(一)

    AOP(Aspect Oriented Programming) 面向切面编程,是Spring框架的一个重要组件. AOP应该算是对OOP(面向对象编程)的补充和完善.OOP引入封装.继承.多态等概念 ...

  2. A1038 Recover the Smallest Number (30 分)

    一.技术总结 此问题是贪心类问题,给出可能有前导零的数字串,将他们按照某个顺序拼接,使生成的数最小. 解决方案,就是使用cmp函数,因为两两字符串进行拼接,进行排序从小到大. 拼接过后会有0可能出现在 ...

  3. B1020 月饼(25分)

    #include<cstdio> #include<algorithm> #include<iostream> using namespace std; struc ...

  4. [LeetCode] 902. Numbers At Most N Given Digit Set 最大为 N 的数字组合

    We have a sorted set of digits D, a non-empty subset of {'1','2','3','4','5','6','7','8','9'}.  (Not ...

  5. 509道Java面试题解析:2020年最新Java面试题

    <Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...

  6. Visual Studio 调试系列3 断点

    系列目录     [已更新最新开发文章,点击查看详细] 断点是开发人员的工具箱中最重要的调试技术之一. 若要暂停调试程序执行所需的位置设置断点. 例如,你可能想要查看代码变量的状态或查看调用堆栈的某些 ...

  7. IIS上传文件大小限制和上传时间限制

    1.打开某一发布网站的配置编辑器 2.设置上传时间限制 3.设置上传文件大小限制 另一种方法: 直接在网站根目录建一个web.config文件 <?xml version="1.0&q ...

  8. elasticsearch配置集群+elk报错总结

    配置ELK的时候,我平常遇到了以下几种报错情况,整理如下(持续更新中): elasticsearch启动失败 # systemctl start elasticsearch Job for elast ...

  9. Kelp.Net是一个用c#编写的深度学习库

    Kelp.Net是一个用c#编写的深度学习库 基于C#的机器学习--c# .NET中直观的深度学习   在本章中,将会学到: l  如何使用Kelp.Net来执行自己的测试 l  如何编写测试 l  ...

  10. torch_09_DCGAN_注意的细节

    DCGAN github链接:https://github.com/darr/DCGAN DCGAN:1.在一次epoch中,如果第i批的i能够整除every_print,则打印到output文件中( ...