最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python并發(fā)編程線程消息通信機(jī)制詳解

 更新時(shí)間:2021年10月25日 12:03:21   作者:寫代碼的明哥  
這篇文章主要為大家介紹了Python并發(fā)編程之線程消息通信機(jī)制的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步

前面我已經(jīng)向大家介紹了,如何使用創(chuàng)建線程,啟動(dòng)線程。相信大家都會(huì)有這樣一個(gè)想法,線程無非就是創(chuàng)建一下,然后再start()下,實(shí)在是太簡單了。

可是要知道,在真實(shí)的項(xiàng)目中,實(shí)際場景可要我們舉的例子要復(fù)雜的多得多,不同線程的執(zhí)行可能是有順序的,或者說他們的執(zhí)行是有條件的,是要受控制的。如果僅僅依靠前面學(xué)的那點(diǎn)淺薄的知識(shí),是遠(yuǎn)遠(yuǎn)不夠的。

那今天,我們就來探討一下如何控制線程的觸發(fā)執(zhí)行。

要實(shí)現(xiàn)對(duì)多個(gè)線程進(jìn)行控制,其實(shí)本質(zhì)上就是消息通信機(jī)制在起作用,利用這個(gè)機(jī)制發(fā)送指令,告訴線程,什么時(shí)候可以執(zhí)行,什么時(shí)候不可以執(zhí)行,執(zhí)行什么內(nèi)容。

經(jīng)過我的總結(jié),線程中通信方法大致有如下三種:

threading.Event

threading.Condition

queue.Queue

接下來我們來一一探討下。

1 Event事件

Python提供了非常簡單的通信機(jī)制 Threading.Event,通用的條件變量。多個(gè)線程可以等待某個(gè)事件的發(fā)生,在事件發(fā)生后,所有的線程都會(huì)被激活。

關(guān)于Event的使用也超級(jí)簡單,就三個(gè)函數(shù)

event = threading.Event()

# 重置event,使得所有該event事件都處于待命狀態(tài)
event.clear()

# 等待接收event的指令,決定是否阻塞程序執(zhí)行
event.wait()

# 發(fā)送event指令,使所有設(shè)置該event事件的線程執(zhí)行
event.set()

舉個(gè)例子來看下。

import time
import threading

class MyThread(threading.Thread):
    def __init__(self, name, event):
        super().__init__()
        self.name = name
        self.event = event

    def run(self):
        print('Thread: {} start at {}'.format(self.name, time.ctime(time.time())))
        # 等待event.set()后,才能往下執(zhí)行
        self.event.wait()
        print('Thread: {} finish at {}'.format(self.name, time.ctime(time.time())))

threads = []
event = threading.Event()

# 定義五個(gè)線程
[threads.append(MyThread(str(i), event)) for i in range(1,5)]

# 重置event,使得event.wait()起到阻塞作用
event.clear()

# 啟動(dòng)所有線程
[t.start() for t in threads]

print('等待5s...')
time.sleep(5)

print('喚醒所有線程...')
event.set()
Thread: 1 start at Sun May 13 20:38:08 2018
Thread: 2 start at Sun May 13 20:38:08 2018
Thread: 3 start at Sun May 13 20:38:08 2018
Thread: 4 start at Sun May 13 20:38:08 2018

等待5s...

喚醒所有線程...
Thread: 1 finish at Sun May 13 20:38:13 2018
Thread: 4 finish at Sun May 13 20:38:13 2018
Thread: 2 finish at Sun May 13 20:38:13 2018
Thread: 3 finish at Sun May 13 20:38:13 2018

可見在所有線程都啟動(dòng)(start())后,并不會(huì)執(zhí)行完,而是都在self.event.wait()止住了,需要我們通過event.set()來給所有線程發(fā)送執(zhí)行指令才能往下執(zhí)行。

2 Condition

Condition和Event 是類似的,并沒有多大區(qū)別。

同樣,Condition也只需要掌握幾個(gè)函數(shù)即可。

cond = threading.Condition()

# 類似lock.acquire()
cond.acquire()

# 類似lock.release()
cond.release()

# 等待指定觸發(fā),同時(shí)會(huì)釋放對(duì)鎖的獲取,直到被notify才重新占有瑣。
cond.wait()

# 發(fā)送指定,觸發(fā)執(zhí)行
cond.notify()

舉個(gè)網(wǎng)上一個(gè)比較趣的捉迷藏的例子來看看

import threading, time

class Hider(threading.Thread):
    def __init__(self, cond, name):
        super(Hider, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        time.sleep(1)  #確保先運(yùn)行Seeker中的方法
        self.cond.acquire()
        print(self.name + ': 我已經(jīng)把眼睛蒙上了')
        self.cond.notify()
        self.cond.wait()
        print(self.name + ': 我找到你了哦 ~_~')
        self.cond.notify() 
        self.cond.release()
        print(self.name + ': 我贏了')

class Seeker(threading.Thread):
    def __init__(self, cond, name):
        super(Seeker, self).__init__()
        self.cond = cond
        self.name = name
        
    def run(self):
        self.cond.acquire()
        self.cond.wait()
        print(self.name + ': 我已經(jīng)藏好了,你快來找我吧')
        self.cond.notify()
        self.cond.wait()
        self.cond.release()
        print(self.name + ': 被你找到了,哎~~~')
        
cond = threading.Condition()
seeker = Seeker(cond, 'seeker')
hider = Hider(cond, 'hider')
seeker.start()
hider.start()

通過cond來通信,阻塞自己,并使對(duì)方執(zhí)行。從而,達(dá)到有順序的執(zhí)行。
看下結(jié)果

hider:   我已經(jīng)把眼睛蒙上了
seeker:  我已經(jīng)藏好了,你快來找我吧
hider:   我找到你了 ~_~
hider:   我贏了
seeker:  被你找到了,哎~~~

3 Queue隊(duì)列

最后一個(gè),隊(duì)列,它是本節(jié)的重點(diǎn),因?yàn)樗俏覀內(nèi)粘i_發(fā)中最使用頻率最高的。

從一個(gè)線程向另一個(gè)線程發(fā)送數(shù)據(jù)最安全的方式可能就是使用 queue 庫中的隊(duì)列了。創(chuàng)建一個(gè)被多個(gè)線程共享的 Queue 對(duì)象,這些線程通過使用put()get() 操作來向隊(duì)列中發(fā)送和獲取元素。

同樣,對(duì)于Queue,我們也只需要掌握幾個(gè)函數(shù)即可。

from queue import Queue
# maxsize默認(rèn)為0,不受限
# 一旦>0,而消息數(shù)又達(dá)到限制,q.put()也將阻塞
q = Queue(maxsize=0)

# 默認(rèn)阻塞程序,等待隊(duì)列消息,可設(shè)置超時(shí)時(shí)間
q.get(block=True, timeout=None)

# 發(fā)送消息:默認(rèn)會(huì)阻塞程序至隊(duì)列中有空閑位置放入數(shù)據(jù)
q.put(item, block=True, timeout=None)

# 等待所有的消息都被消費(fèi)完
q.join()

# 通知隊(duì)列任務(wù)處理已經(jīng)完成,當(dāng)所有任務(wù)都處理完成時(shí),join() 阻塞將會(huì)解除
q.task_done()

以下三個(gè)方法,知道就好,一般不需要使用

# 查詢當(dāng)前隊(duì)列的消息個(gè)數(shù)
q.qsize()

# 隊(duì)列消息是否都被消費(fèi)完,返回 True/False
q.empty()

# 檢測隊(duì)列里消息是否已滿
q.full()

函數(shù)會(huì)比之前的多一些,同時(shí)也從另一方面說明了其功能更加豐富。

我來舉個(gè)老師點(diǎn)名的例子。

# coding=utf-8
# /usr/bin/env python

'''
Author: wangbm
Email: wongbingming@163.com
Wechat: mrbensonwon
Blog: python-online.cn
公眾號(hào):Python編程時(shí)光
date: 2020/9/20 下午7:30
desc: 
'''
__author__ = 'wangbm'
from queue import Queue
from threading import Thread
import time
class Student:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print("{}:到!".format(self.name))

class Teacher:
    def __init__(self, queue):
        super().__init__()
        self.queue=queue

    def call(self, student_name):
        if student_name == "exit":
            print("點(diǎn)名結(jié)束,開始上課..")
        else:
            print("老師:{}來了沒?".format(student_name))
            # 發(fā)送消息,要點(diǎn)誰的名
        self.queue.put(student_name)

class CallManager(Thread):
    def __init__(self, queue):
        super().__init__()
        self.students = {}
        self.queue = queue

    def put(self, student):
        self.students.setdefault(student.name, student)

    def run(self):
        while True:
            # 阻塞程序,時(shí)刻監(jiān)聽老師,接收消息
            student_name = queue.get()
            if student_name == "exit":
                break
            elif student_name in self.students:
                self.students[student_name].speak()
            else:
                print("老師,咱班,沒有 {} 這個(gè)人".format(student_name))

queue = Queue()
teacher = Teacher(queue=queue)
s1 = Student(name="小明")
s2 = Student(name="小亮")
cm = CallManager(queue)
cm.put(s1)
cm.put(s2)
cm.start()
print('開始點(diǎn)名~')
teacher.call('小明')
time.sleep(1)
teacher.call('小亮')
time.sleep(1)
teacher.call("exit")

運(yùn)行結(jié)果如下

開始點(diǎn)名~
老師:小明來了沒?
小明:到!
老師:小亮來了沒?
小亮:到!
點(diǎn)名結(jié)束,開始上課..

其實(shí) queue 還有一個(gè)很重要的方法,Queue.task_done()

如果不明白它的原理,我們?cè)趯懗绦?,就很有可能卡死?/p>

當(dāng)我們使用 Queue.get() 從隊(duì)列取出數(shù)據(jù)后,這個(gè)數(shù)據(jù)有沒有被正常消費(fèi),是很重要的。

如果數(shù)據(jù)沒有被正常消費(fèi),那么Queue會(huì)認(rèn)為這個(gè)任務(wù)還在執(zhí)行中,此時(shí)你使用 Queue.join() 會(huì)一直阻塞,即使此時(shí)你的隊(duì)列里已經(jīng)沒有消息了。

那么如何解決這種一直阻塞的問題呢?

就是在我們正常消費(fèi)完數(shù)據(jù)后,記得調(diào)用一下 Queue.task_done(),說明隊(duì)列這個(gè)任務(wù)已經(jīng)結(jié)束了。

當(dāng)隊(duì)列內(nèi)部的任務(wù)計(jì)數(shù)器歸于零時(shí),調(diào)用 Queue.join() 就不會(huì)再阻塞了。

要理解這個(gè)過程,請(qǐng)參考 http://python.iswbm.com/en/latest/c02/c02_06.html 里自定義線程池的的例子。

4 總結(jié)一下

學(xué)習(xí)了以上三種通信方法,我們很容易就能發(fā)現(xiàn)EventCondition 是threading模塊原生提供的模塊,原理簡單,功能單一,它能發(fā)送 TrueFalse 的指令,所以只能適用于某些簡單的場景中。

Queue則是比較高級(jí)的模塊,它可能發(fā)送任何類型的消息,包括字符串、字典等。其內(nèi)部實(shí)現(xiàn)其實(shí)也引用了Condition模塊(譬如putget函數(shù)的阻塞),正是其對(duì)Condition進(jìn)行了功能擴(kuò)展,所以功能更加豐富,更能滿足實(shí)際應(yīng)用。

以上就是Python并發(fā)編程線程消息通信機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于Python并發(fā)線程消息通信機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python requests post的使用方式

    python requests post的使用方式

    這篇文章主要介紹了python requests post的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • python中圖像通道分離與合并實(shí)例

    python中圖像通道分離與合并實(shí)例

    今天小編就為大家分享一篇python中圖像通道分離與合并實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-01-01
  • python單線程實(shí)現(xiàn)多個(gè)定時(shí)器示例

    python單線程實(shí)現(xiàn)多個(gè)定時(shí)器示例

    這篇文章主要介紹了python單線程實(shí)現(xiàn)多個(gè)定時(shí)器示例,需要的朋友可以參考下
    2014-03-03
  • Python 循環(huán)函數(shù)詳細(xì)介紹

    Python 循環(huán)函數(shù)詳細(xì)介紹

    循環(huán)用于重復(fù)執(zhí)行一些程序塊。從上一講的選擇結(jié)構(gòu),我們已經(jīng)看到了如何用縮進(jìn)來表示程序塊的隸屬關(guān)系。循環(huán)也會(huì)用到類似的寫法。感興趣得小伙伴請(qǐng)參考下面文字得具體內(nèi)容
    2021-09-09
  • 使用with torch.no_grad():顯著減少測試時(shí)顯存占用

    使用with torch.no_grad():顯著減少測試時(shí)顯存占用

    這篇文章主要介紹了使用with torch.no_grad():顯著減少測試時(shí)顯存占用問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Python工程師面試必備25條知識(shí)點(diǎn)

    Python工程師面試必備25條知識(shí)點(diǎn)

    這篇文章主要為大家詳細(xì)介紹了Python工程師面試必備25條知識(shí)點(diǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • python爬取基于m3u8協(xié)議的ts文件并合并

    python爬取基于m3u8協(xié)議的ts文件并合并

    這篇文章主要為大家詳細(xì)介紹了python爬取基于m3u8協(xié)議的ts文件并合并,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • python接口,繼承,重載運(yùn)算符詳解

    python接口,繼承,重載運(yùn)算符詳解

    這篇文章主要給大家介紹了關(guān)于Python接口,繼承,重載運(yùn)算符的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Python實(shí)現(xiàn)以時(shí)間換空間的緩存替換算法

    Python實(shí)現(xiàn)以時(shí)間換空間的緩存替換算法

    緩存是指可以進(jìn)行高速數(shù)據(jù)交換的存儲(chǔ)器,它先于內(nèi)存與CPU交換數(shù)據(jù),因此速度很快。緩存就是把一些數(shù)據(jù)暫時(shí)存放于某些地方,可能是內(nèi)存,也有可能硬盤。下面給大家介紹Python實(shí)現(xiàn)以時(shí)間換空間的緩存替換算法,需要的朋友參考下
    2016-02-02
  • python 元組的使用方法

    python 元組的使用方法

    這篇文章主要介紹了python 元組的使用方法,文中講解非常細(xì)致,代碼幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06

最新評(píng)論

乌兰浩特市| 镇赉县| 阳泉市| 资中县| 永善县| 桑日县| 临高县| 清丰县| 井研县| 教育| 沙湾县| 太白县| 福州市| 灵丘县| 探索| 湛江市| 屏东县| 庐江县| 海盐县| 淮阳县| 新邵县| 扎囊县| 额敏县| 桂东县| 武安市| 彰化市| 海林市| 米易县| 汪清县| 茶陵县| 星座| 宜春市| 富源县| 婺源县| 个旧市| 房产| 江山市| 大埔县| 麦盖提县| 怀柔区| 两当县|