一個場景
在界面編程中,我們往往有很多按鈕,當(dāng)按下某個按鈕就觸發(fā)一個事件。
import time
def btnClicked():
print "handle click event"
time.sleep(5)
print "event handled"
btnClicked()
print "another event happend"
當(dāng)btnClicked()完成后(5秒后),才打印出"another event happend",也就是說當(dāng)按鈕按下后,整個界面就被阻塞了,點哪兒都沒有用。
線程
解決方案是開一個線程,把這個按鈕背后的事件放在這個線程里。
import time
def btnClicked():
print "handle click event"
time.sleep(5)
print "event handled"
from threading import Thread
t = Thread(target=btnClicked)
t.start()
print "another event happend"
我們把按鈕觸發(fā)的事件放在一個線程中執(zhí)行,這樣主程序可以保持active狀態(tài)
進(jìn)程
我們也可以開個進(jìn)程,把一些計算任務(wù)扔到新進(jìn)程上去計算
import multiprocessing
p = multiprocessing.Process(target=btnClicked)
p.start()
GIL
python由于GIL的存在,線程的執(zhí)行模型被限制為只能在解釋器中運行一個線程。所以,python線程不適合處理計算密集型任務(wù),而是適合IO密集型的任務(wù)。
記住這句話:進(jìn)程是資源分配的基本單位,線程是資源調(diào)度的基本單位
線程間通信
我們用queue.Queue來實現(xiàn)線程間的通信
from queue import Queue
from threading import Thread
def producer(out_q):
n = 10
while n>0:
print "produce data {}".format(n)
out_q.put(n)
n -= 1
def consumer(in_q):
while True:
data = in_q.get()
print "consume data {}".format(data)
if data == 1:
break
q = Queue()
t1 = Thread(target=consumer, args=(q,))
t2 = Thread(target=producer, args=(q,))
t1.start()
t2.start()
上面的代碼中,我們有一個生產(chǎn)者一個消費者,生產(chǎn)者生產(chǎn)數(shù)據(jù)并put進(jìn)隊列,而消費者從隊列里面拉出數(shù)據(jù)并處理。這個例子里面只有一個消費者。當(dāng)然我們也可以生成幾個消費者。
線程池
在某些場景中(比如爬蟲),我們有大量的io請求,如果對每個請求都單獨開一個線程,很可能會產(chǎn)生無數(shù)的線程數(shù),從而耗盡系統(tǒng)資源。更好的方法是,我們先初始化好有一定數(shù)量線程對象的線程池,當(dāng)有任務(wù)要處理并且有空余線程時,才在空余線程上跑任務(wù)。
from multiprocessing.dummy import Pool as ThreadPool
import urllib2
pool = ThreadPool(5)
urls = ["https://v2ex.com", "https://www.v2ex.com/?tab=play",
"https://www.v2ex.com/?tab=jobs","https://www.v2ex.com/?tab=tech",
"https://www.v2ex.com/?tab=creative","https://www.v2ex.com/?tab=deals",
"https://www.v2ex.com/?tab=city", "https://www.v2ex.com/?tab=qna"]
result = pool.map(lambda url: urllib2.urlopen(url), urls)
print result