一、事件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机制来协调各个工作线程的连接操作

  1. from threading import Event,Thread
  2. import time
  3. import random


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

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

  20. e = Event()
  21. t1 = Thread(target=check_web,args=(e,))
  22. t2 = Thread(target=connect_db,args=(e,))
  23. t1.start()
  24. 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()

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

例子


  1. from threading import Thread,Condition

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


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

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

  

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

三、定时器Timer

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

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

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

Timer()

interval 第一个参数传 间隔时间

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

给函数传参方式 args kwargs

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

例子


  1. from threading import Timer
  2. import time


  3. def add(x, y):
  4. print(x + y)


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

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

  

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

例子

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

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


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

  

  1.  

四、线程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()解除阻塞。

三种不同的队列类

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

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

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

例子

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


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

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

  

生产者消费者模型

  1. import threading
  2. import time
  3. from queue import Queue


  4. def put_id():
  5. i = 0
  6. while True:
  7. i = i + 1
  8. print("添加数据", i, id_queue.qsize())
  9. time.sleep(1)
  10. id_queue.put(i)


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


  15. if __name__ == "__main__":
  16. id_queue = Queue(10)

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

  22. Th1.start()
  23. Th2.start()
  24. Th3.start()
  25. Th4.start()
  26. 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. 解决Android中AsyncTask的多线程阻塞问题

    Android开发中执行耗时操作并更新UI时,通常有三种方式:1.直接调用runOnUiThread(new Runnable(){}),使用简单,但不能在Activity之外的环境使用,如View. ...

  2. GAN与VAE

    经典算法·GAN与VAE Generative Adversarial Networks 及其变体 生成对抗网络是近几年最为经典的生成模型的代表工作,Goodfellow的经典工作.通过两个神经网络结 ...

  3. Matlab:Toeplitz矩阵-向量乘法的快速傅里叶(FFT)算法

    一.$\tt Toeplitz$矩阵与循环($\tt Circulant$)矩阵 定义 为$n\times n$阶循环矩阵. 定义 $T_n(i,j)=t_{j-i} $  为$n\times n$ ...

  4. MySQL-InnoDB-MVCC多版本并发控制

    一.MySQL可重复读级别下,因为MVCC引起的BUG,下图1为相应的Java代码,其中事务1的生命周期最长,循环开启的事务2.3.4...与事务1并行 ,数据的读取只会成功一次,后面的读不到新增数据 ...

  5. boolean 属性的定义规范

    [强制]POJO类中的任何布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误.说明:在本文MySQL规约中的建表约定第一条,表达是与否的值采用is_xxx的命名方式,所以,需要在< ...

  6. # Leetcode 14:Longest Common Prefix 最长公共前缀

    公众号:爱写bug Write a function to find the longest common prefix string amongst an array of strings. If ...

  7. 基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒)

    本篇文章给大家带来的内容是关于基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 公司需要实现X分钟内每隔Y秒 ...

  8. Vue双向绑定原理(我尽量写的。简洁)

    先问自己两个问题: 1.app.message修改数据的时候,Vue内部是如何监听message数据发生改变的 使用Object.defineProperty ->监听对象属性的改变 2.当数据 ...

  9. 用pyenv管理Python多版本及下载加速方法--Mac上

    原文:https://www.jianshu.com/p/91fc7ecc5e46 先大致介绍下pyenv的安装及配置流程.随后介绍加速下载方法 安装: brew install pyenv 配置 在 ...

  10. Redis(三)数据类型

    之前的文章中说了Redis的常见应用场景和特性,在特性章节中也大致说了数据结构契合场景.因为我想在更深入.更全面的学习Redis之前,了解场景和特性,才能在学习时更加全面且理解更透彻: redis的什 ...