一日一技:Python多線程的事件監(jiān)控
設(shè)想這樣一個(gè)場(chǎng)景:
你創(chuàng)建了10個(gè)子線程,每個(gè)子線程分別爬一個(gè)網(wǎng)站,一開始所有子線程都是阻塞等待。一旦某個(gè)事件發(fā)生:例如有人在網(wǎng)頁上點(diǎn)了一個(gè)按鈕,或者某人在命令行輸入了一個(gè)命令,10個(gè)爬蟲同時(shí)開始工作。
肯定有人會(huì)想到用Redis來實(shí)現(xiàn)這個(gè)開關(guān):所有子線程全部監(jiān)控Redis中名為start_crawl的字符串,如果這個(gè)字符串不存在,或者為0,那么就等待1秒鐘,再繼續(xù)檢查。如果這個(gè)字符串為1,那么就開始運(yùn)行。
代碼片段可以簡寫為:
- import time
- import redis
- client = redis.Redis()
- while client.get('start_crawl') != 1:
- print('繼續(xù)等待')
- time.sleep(1)
這樣做確實(shí)可以達(dá)到目的,不過每一個(gè)子線程都會(huì)頻繁檢查Redis。
實(shí)際上,在Python的多線程中,有一個(gè)Event模塊,天然就是用來實(shí)現(xiàn)這個(gè)目的的。
Event是一個(gè)能在多線程中共用的對(duì)象,一開始它包含一個(gè)為False的信號(hào)標(biāo)志,一旦在任一一個(gè)線程里面把這個(gè)標(biāo)記改為True,那么所有的線程都會(huì)看到這個(gè)標(biāo)記變成了True。
我們通過一段代碼來說明它的使用方法:
- import threading
- import time
- class spider(threading.Thread):
- def __init__(self, n, event):
- super().__init__()
- self.n = n
- self.event = event
- def run(self):
- print(f'第{self.n}號(hào)爬蟲已就位!')
- self.event.wait()
- print(f'信號(hào)標(biāo)記變?yōu)門rue!!第{self.n}號(hào)爬蟲開始運(yùn)行')
- eve = threading.Event()
- for num in range(10):
- crawler = spider(num, eve)
- crawler.start()
- input('按下回車鍵,啟動(dòng)所有爬蟲!')
- eve.set()
- time.sleep(10)
運(yùn)行效果如下圖所示:
在這段代碼中,線程spider在運(yùn)行以后,會(huì)運(yùn)行到self.event.wait()這一行,然后10個(gè)子線程會(huì)全部阻塞在這里。而這里的self.event,就是主線程中eve = threading.Event()生成的對(duì)象傳入進(jìn)去的。
在主線程里面,當(dāng)執(zhí)行了eve.set()后,所有子線程的阻塞會(huì)被同時(shí)解除,于是子線程就可以繼續(xù)運(yùn)行了。
本文轉(zhuǎn)載自微信公眾號(hào)「未聞Code」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系未聞Code公眾號(hào)。