Python之路(第四十五篇)线程Event事件、 条件Condition、定时器Timer、线程queue

一、事件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()

  

原文地址:https://www.cnblogs.com/Nicholas0707/p/11441033.html

时间: 2024-08-07 18:41:59

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

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

Python之路[第十五篇]:Web框架 Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #!/usr/bin/env python #coding:utf-8   import socket   def handle_request(client):     buf = client.recv(10

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

一.线程相关的其他方法 Thread实例对象的方法 # isAlive(): 返回线程是否活动的. # getName(): 返回线程名. # setName(): 设置线程名. ? threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程对象. # threading.enumerate(): 返回一个包含正在运行的线程的list.正在运行指线程启动后.结束前,不包括启动前和终止后的线程. # threading.activeCount(

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

Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:utf-8 import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, Seven")

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

要求: 1.用户加密认证 2.允许同时多用户登录 3.每个用户有自己的家目录,且只能访问自己的家目录 4.对用户进行磁盘配额,每个用户的可用空间不同 5.允许用户在ftp server上随意切换目录 6.允许用户查看当前目录下的文件 7.允许上传和下载文件,并保证文件的一致性md5 8.文件传输过程中显示进度条 9.支持文件的断点续传 使用:1.启动ftp_server.py2.创建用户,输入:用户名(默认密码是zhurui)3.启动FTP服务器4.启动客户端ftp_client.py5.输入用

Python之路【第十八篇】:Web框架们

Python之路[第十八篇]:Web框架们 Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Python的标准库外,其不依赖任何其他模块. 1 2 3 4 pip install bottle easy_install bottle apt-get install python-bottle wget http://bottlepy.org/bottle.py Bottle框架大致可以分为以下部分: 路

【Python之路】第十八篇--MySQL(一)

[Python之路]第十八篇--MySQL(一) 一.概述 1.什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access.MS SQL Server等 ? 答:他们均是一个软件,都有两个主要的功能: a. 将数据保存到文件或内存 b. 接收特定的命令,然后对文件进行相应的操作 PS:如果有了以上软件,无须自己再去创建文件和文件夹,而是直接传递 命令 给上述软件,让其来进行文件操作,他们统称为数

Python之路【第十六篇】:Django【基础篇】

Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. 基本配置 一.创建django程序 终端命令:django-admin startproject sitename IDE创建Django程序时,本质上都是自动执行上述命令 其他常用命令: python manage.py runserver

Python之路【第十九篇】:爬虫

Python之路[第十九篇]:爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕虫. Requests Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务. import

秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题 在<秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量>中对经典多线程同步互斥问题进行了回顾和总结,这篇文章对Windows系统下常用的线程同步互斥机制——关键段.事件.互斥量.信号量进行了总结.有网友问到互斥量能处理“遗弃”问题,事件和信号量是否也能处理“遗弃”问题.因此本文将对事件和信号量作个试验,看看事件和信号量能否处理“遗弃”问题. 一.