Python中輪詢觸發(fā)更替事件驅(qū)動的簡單方法

在處理事件隊列的過程中不少情況是采用輪詢的方式進行的奔脐。

例如如下例子所示躯舔。在Example中彭雾,主線程和子線程通過隊列的形式進行通信,為模擬業(yè)務串稀,主線程將隨時獲取到的待處理任務放入對應的任務隊列(將一隨機數(shù)放入隨機的隊列中除抛,之后等待隨機的時間),子線程發(fā)現(xiàn)有隊列中有待處理的事件就將其取出進行處理(取出隊列中的數(shù)字進行打印)母截。

import time
from collections import deque
from random import Random
import threading

class Example():

    def __init__(self):
        self.r = Random(time.time())

    def worker_run(self, name, q):
        while True:
            if q:
                print("%s pop number: %s, %s numbers left" % (name, q.pop(), len(q)))
            time.sleep(0.001)

    def manager_run(self,worker_num):
        queue_list = [deque() for i in range(worker_num)]
        
        worker_list = [threading.Thread(target=self.worker_run, 
            args=["Thread-%s" % i, queue_list[i]]) for i in range(worker_num)]
        for worker in worker_list:
            worker.start()
        while True:
            i = 0
            while i< self.r.randint(0,20):
                queue_list[self.r.randint(0,worker_num - 1)].appendleft(
                        self.r.randint(1,100)
                    )
                i += 1
            time.sleep(self.r.random()/100)

if __name__ == '__main__':
    Example().manager_run(3)

進行功能分離簡單到忽,目的性明確。但是存在效率性的問題微酬,如果想提高觸發(fā)效率绘趋,那么需要將worker_run中的每輪sleep時間設置的非常小颤陶,甚至是0。這么就造成了另一個問題陷遮,CPU的損耗問題滓走。無意義的while死循環(huán)無疑會造成CPU的無意義損耗。這個對機器性能消耗非常巨大的帽馋。所以搅方,需要對機制進行修改,由輪詢轉(zhuǎn)為事件驅(qū)動绽族。這樣的更改無疑會對代碼整體結構有較大的調(diào)整姨涡。但是是否有簡單的方式可以快速達到目的,且更代碼改較量小吧慢。

不難想到涛漂,只要在事件隊列為空時暫停工作線程,隊列放入值時重啟工作線程即可检诗。原本匈仗,直接對線程進行中斷和繼續(xù)是最好的解決方案,但是在python中threading.Thread并不支持逢慌。在官方文檔如是說:

The design of this module is loosely based on Java’s threading model. However, where Java makes locks and condition variables basic behavior of every object, they are separate objects in Python. Python’s Thread
class supports a subset of the behavior of Java’s Thread class; currently, there are no priorities, no thread groups, and threads cannot be destroyed, stopped, suspended, resumed, or interrupted. The static methods of Java’s Thread class, when implemented, are mapped to module-level functions.

這意味著悠轩,沒有一種方法直接對子線程進行暫停、重啟等攻泼。所以換了一個思路火架,使用threading.Event實現(xiàn)信號量作為暫停重啟的替代方案。當需要暫停時忙菠,使用threading.Event.wait()等待信號何鸡。當需要繼續(xù)時,使用threading.Event.set()發(fā)出信號只搁。這樣就解決了線程暫停音比、繼續(xù)的問題。

另一個問題是如何在隊列發(fā)生改變時觸發(fā)信號量的更改氢惋,不同的變量可以通過不同的方式實現(xiàn),事實上每一種變量都用不同的解決方案稽犁。

  • 對于內(nèi)部實例化的示例可以通過繼承重新構建同名class的方式焰望,重寫修改內(nèi)容的方法實現(xiàn)觸發(fā)信號量。
  • 外部變量則可以通過methodType的方式綁定重寫對于的方法已亥。
  • 實例的基礎類型屬性則可以通過@property的方式在setter中加入信號觸發(fā)熊赖。
  • ...

所以可以對上述示例進行如下修改。

import time
from collections import deque as dq
from random import Random
import threading

#重寫用于事件隊列的deque虑椎,使得對deque的變動會觸發(fā)信號量
class deque(dq):
    #存儲信號的集合
    sign = set()
    #重寫append震鹉、appendleft俱笛、extend、extendleft方法
    def append(self, *args, **kwargs):
        dq.append(self, *args, **kwargs)
        self.sign_set()

    def appendleft(self, *args, **kwargs):
        dq.appendleft(self, *args, **kwargs)
        self.sign_set()
        
    def extend(self, *args, **kwargs):
        dq.extend(self, *args, **kwargs)
    
    def extendleft(self, *args, **kwargs):
        dq.extendleft(self, *args, **kwargs)

    #觸發(fā)信號
    def sign_set(self):
        for s in self.sign:
            s.set()

    #添加一個信號
    def add_sign(self,s):
        self.sign.add(s)

class Example():

    def __init__(self):
        self.r = Random(time.time())
    
    #添加一個信號量綁定給deque传趾,當發(fā)現(xiàn)deque中沒有值之后等待信號傳入
    def worker_run(self, name, q):
        sign = threading.Event()
        q.add_sign(sign)
        while True:
            if q:
                print("%s pop number: %s, %s numbers left" % (name, q.pop(), len(q)))
            else:
                sign.wait()
                sign.clear()

    def manager_run(self,worker_num):
        queue_list = [deque() for i in range(worker_num)]
        
        worker_list = [threading.Thread(target=self.worker_run, 
            args=["Thread-%s" % i, queue_list[i]]) 
            for i in range(worker_num)]
        for worker in worker_list:
            worker.start()
        while True:
            i = 0
            while i< self.r.randint(0,20):
                queue_list[self.r.randint(0,worker_num - 1)].appendleft(
                        self.r.randint(1,100)
                    )
                i += 1
            time.sleep(self.r.random())

if __name__ == '__main__':
    Example().manager_run(3)

通過這種方式最小限度的更改代碼迎膜,幾乎無代碼結構變動。就可以將輪詢觸發(fā)變?yōu)槭录?qū)動浆兰,在不影響原有業(yè)務的前提下降低系統(tǒng)的無意義負載磕仅。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市簸呈,隨后出現(xiàn)的幾起案子榕订,更是在濱河造成了極大的恐慌,老刑警劉巖蜕便,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劫恒,死亡現(xiàn)場離奇詭異,居然都是意外死亡轿腺,警方通過查閱死者的電腦和手機两嘴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吃溅,“玉大人溶诞,你說我怎么就攤上這事【龀蓿” “怎么了螺垢?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赖歌。 經(jīng)常有香客問我枉圃,道長,這世上最難降的妖魔是什么庐冯? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任孽亲,我火速辦了婚禮,結果婚禮上展父,老公的妹妹穿的比我還像新娘返劲。我一直安慰自己,他們只是感情好栖茉,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布篮绿。 她就那樣靜靜地躺著,像睡著了一般吕漂。 火紅的嫁衣襯著肌膚如雪亲配。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機與錄音吼虎,去河邊找鬼犬钢。 笑死,一個胖子當著我的面吹牛思灰,可吹牛的內(nèi)容都是我干的玷犹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼官辈,長吁一口氣:“原來是場噩夢啊……” “哼箱舞!你這毒婦竟也來了?” 一聲冷哼從身側響起拳亿,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤晴股,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肺魁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體电湘,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年鹅经,在試婚紗的時候發(fā)現(xiàn)自己被綠了寂呛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘾晃,死狀恐怖贷痪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蹦误,我是刑警寧澤劫拢,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站强胰,受9級特大地震影響舱沧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜偶洋,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一熟吏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玄窝,春花似錦牵寺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至东亦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背典阵。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工奋渔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壮啊。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓嫉鲸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親歹啼。 傳聞我的和親對象是個殘疾皇子玄渗,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容