1. 程式人生 > >11.python並發入門(part5 event對象)

11.python並發入門(part5 event對象)

python event

一、引入event。

每個線程,都是一個獨立運行的個體,並且每個線程的運行狀態是無法預測的。

如果一個程序中有很多個線程,程序的其他線程需要判斷某個線程的運行狀態,來確定自己下一步要執行哪些操作。

threading模塊中的event對象恰好能做到這一點,event對象包含了一個可以通過線程設置的一個信號標誌位,它允許線程一直等待某些事件的發生。

在初始化默認的情況下,event對象中的信號標識被設置為“假”,如果這時,有一個線程等待這個event對象,而這個event對象的信號標誌為“假”,那麽這個線程就會被一直阻塞下去,直到這個event信號標誌為“真”,所有等待這個event對象的線程才會被喚醒。

也就是說,一個線程如果等待的event對象中的信號標誌位“真”,這個線程會忽略這個事件(event),繼續執行。


二、event對象的常用方法。

event.isSet():返回event的狀態值。

event.wait(): 當event對象內部的信號標識設置為“假”(False),所有等待這個event對象的線程將會全部被阻塞。


event.set():設置event對象內部的信號標識為“真”(True),所有等待這個event對象的線程將會被喚醒,等待操作系統的調度。


event.clear():恢復event的狀態值為False。


下面這個圖,能讓你清楚的明白event.wait和event.set之間的關系:

技術分享


三、event使用示例。

我們來考慮下event對象的應用場景。

假如,在一個程序中,有多個線程需要到redis中來取數據,每個線程都要去嘗試著去連接redis服務器,一般情況下,redis如果連接不成功,在所有線程中都需要嘗試著去重新連接。

如果我們想要在程序啟動時,先要確保redis服務器工作正常,接著在讓其他工作的線程去連接redis服務器,當遇到這種需求的時候,使用event對象就是一個非常不錯的選擇。

我們可以使用event機制去讓各個工作的線程去做一個簡單的通信,主線程去連接redis服務器,如果連接到了,event對象內的信號標識改為真,其余工作的線程就會被喚醒。


示例:

#!/usr/local/bin/python2.7

# -*- coding:utf-8 -*-

import threading

import time

import logging

logging.basicConfig(level=logging.DEBUG, format=‘(%(threadName)-10s) %(message)s‘,)

def worker(event):

logging.debug("waiting for redis ready....")

event.wait()

logging.debug("redis ready,and connect to redis server and do some work %s",time.ctime())

time.sleep(1)

def main():

redis_ready = threading.Event()

t1 = threading.Thread(target=worker,args=(redis_ready,),name="threading-1")

t1.start()

t2 = threading.Thread(target=worker,args=(redis_ready,),name="threading-2")

t2.start()

logging.debug("first of all,check redis server, make sure it is ok , and then trigger the redis ready event")

time.sleep(3)

redis_ready.set()

if __name__ == ‘__main__‘:

main()


輸出結果:

(threading-1) waiting for redis ready....

(threading-2) waiting for redis ready....

(MainThread) first of all,check redis server, make sure it is ok , and then trigger the redis ready event

(threading-1) redis ready,and connect to redis server and do some work Sun May 14 09:35:46 2017

(threading-2) redis ready,and connect to redis server and do some work Sun May 14 09:35:46 2017


代碼分析:

在上面這段代碼中,一共開了三個線程,分別是threading-1和threading-2還有主線程,首先主線程運行了main()函數,產生了一個event對象,這個event對象信號的初始值是False,然後創建了threading-1和threading-2兩個線程並且運行,輸出一條日誌first of all,check redis server, make sure it is ok , and then trigger the redis ready event後sleep 3秒,此時切換到了threading-1,threading-1首先執行了worker函數,輸出一次日誌,然後執行event.wait方法,當這個event對象的信號標識為False時,threading-1就被阻塞住了,暫時無法執行woker函數後面的內容,當threading-1被阻塞住後,隨即切換到threading-2(為什麽一定切換到threading-2呢?這是因為主線程還在sleep),threading-2此時也去執行woker函數,同樣先輸出一條日誌waiting for redis ready....,當執行到event.wait時,這個event對象的信號標誌位依舊為False,所以執行到這裏的時候,threading-2也會被阻塞。

過了3秒後,MainThread主線程sleep結束,開始向下運行,執行了redis_ready.set()後,我們創建的那個名為redis_ready的event對象中的信號標誌會變為True,當這event對象的信號標誌變為True之後,之前阻塞的threading-1和threading-2都會被喚醒,繼續執行woker函數後面的代碼。

(threading-1) redis ready,and connect to redis server and do some work Sun May 14 09:35:46 2017

(threading-2) redis ready,and connect to redis server and do some work Sun May 14 09:35:46 2017


四、event對象的補充。

threading.Event的wait方法還接受一個超時參數,默認情況下如果事件一致沒有發生,wait方法會一直阻塞下去,而加入這個超時參數之後,如果阻塞時間超過這個參數設定的值之後,wait方法會返回。對應於上面的應用場景,如果Redis服務器一致沒有啟動,我們希望子線程能夠打印一些日誌來不斷地提醒我們當前沒有一個可以連接的Redis服務,我們就可以通過設置這個超時參數來達成這樣的目的:


def worker(event):

while not event.is_set():

logging.debug(‘Waiting for redis ready...‘)

event.wait(2)

logging.debug(‘redis ready, and connect to redis server and do some work [%s]‘, time.ctime())

time.sleep(1)


本文出自 “reBiRTH” 博客,請務必保留此出處http://suhaozhi.blog.51cto.com/7272298/1925428

11.python並發入門(part5 event對象)