一般讓爬蟲在一個程序内多線程并發,有幾種方法:
run the callable object in a separate thread.
cause a function to be executed by the reactor thread.
也就是說,reactor.callfromthread 是在由 reactor.run() 激發的主消息循環(main event loop)中執行,是以也就能被 reactor.stop() 終止執行。甚至可以通過:
來主動要求主消息循環關閉 reactor 的主線程運作。
callfromthread 有時候比較危險,如果壓的任務太多,會阻塞主消息循環,造成其他事件無法得到及時的處理。
參考 callinthread 的代碼,可以看出它是在 reactor 的一個私有線程池裡工作的:
def callinthread(self, _callable, *args, **kwargs):
if self.threadpool is none:
self._initthreadpool()
self.threadpool.callinthread(_callable, *args, **kwargs)
是以,我們可以通過
這裡有兩個問題:
1、如何通知 callinthread 執行任務的線程退出呢,如何確定線程池内的工作線程安全退出呢?
2、如果讓工作線程去某網站抓取頁面,由于 tcp/ip 的不确定性,可能該工作線程挂起,長時間不傳回。如果線程池内的每一個線程被這樣耗盡,沒有空閑線程,就相當于抓取全部停止了。某個線程或許會因請求逾時而退出,但這也未必可靠。一般通過代碼:
import timeoutsocket
timeoutsocket.setdefaultsockettimeout(120)
設定 socket 逾時時間,但有時候就是會莫名其妙地挂住線程。
twisted.internet.threads.defertothread 與 callinthread 一樣,預設用 reactor.getthreadpool() 所開辟的線程池。它調用這個線程池的 threadpool.callinthreadwithcallback 方法,實際效果和 reactor.callinthread 一樣。差別隻是 defertothread 可以傳回一個deferred對象,進而允許你設定回調函數。
示範代碼:
def finish_success(request):
pass
threads.defertothread(parsedata, body).addcallback(lambda x: finish_success(request))
twisted還提供了一個簡易辦法
return a deferred that has already had '.callback(result)' called.
this is useful when you're writing synchronous code to an asynchronous interface: i.e., some code is calling you expecting a deferred result, but you don't actually need to do anything asynchronous. just return defer.succeed(theresult).
defer.succeed 說白了就是為了讓某函數 a 傳回一個 deferred 對象,進而讓 a.addcallback(…) 異步觸發成為現實。