關(guān)于并發(fā)、并行、同步阻塞、異步非阻塞、線程、進(jìn)程、協(xié)程等這些概念,單純通過文字恐怕很難有比較深刻的理解,本文就通過代碼一步步實(shí)現(xiàn)這些并發(fā)和異步編程,并進(jìn)行比較。解釋器方面本文選擇python3,畢竟python3才是python的未來(lái),并且python3用原生的庫(kù)實(shí)現(xiàn)協(xié)程已經(jīng)非常方便了。
1、準(zhǔn)備階段
下面為所有測(cè)試代碼所需要的包
#! python3# coding:utf-8import socketfrom concurrent import futuresfrom selectors import DefaultSelector,EVENT_WRITE,EVENT_READimport asyncioimport aiohttpimport timefrom time import ctime
在進(jìn)行不同實(shí)現(xiàn)方式的比較時(shí),實(shí)現(xiàn)場(chǎng)景就是在進(jìn)行爬蟲開發(fā)的時(shí)候通過向?qū)Ψ骄W(wǎng)站發(fā)起一系列的http請(qǐng)求訪問,統(tǒng)計(jì)耗時(shí)來(lái)判斷實(shí)現(xiàn)方式的優(yōu)劣,具體地,通過建立通信套接字,訪問新浪主頁(yè),返回源碼,作為一次請(qǐng)求。先實(shí)現(xiàn)一個(gè)裝飾器用來(lái)統(tǒng)計(jì)函數(shù)的執(zhí)行時(shí)間:
def tsfunc(func): def wrappedFunc(*args,**kargs): start = time.clock() action = func(*args,**kargs) time_delta = time.clock() - start print ('[{0}] {1}() called, time delta: {2}'.format(ctime(),func.__name__,time_delta)) return action return wrappedFunc
輸出的格式為:當(dāng)前時(shí)間,調(diào)用的函數(shù),函數(shù)的執(zhí)行時(shí)間。
2、阻塞/非阻塞和同步/異步
這兩對(duì)概念不是很好區(qū)分,從定義上理解:
阻塞:在進(jìn)行socket通信過程中,一個(gè)線程發(fā)起請(qǐng)求,如果當(dāng)前請(qǐng)求沒有返回結(jié)果,則進(jìn)入sleep狀態(tài),期間線程掛起不能做其他操作,直到有返回結(jié)果,或者超時(shí)(如果設(shè)置超時(shí)的話)。
非阻塞:與阻塞相似,只不過在等待請(qǐng)求結(jié)果時(shí),線程并不掛起而是進(jìn)行其他操作,即在不能立刻得到結(jié)果之前,該函數(shù)不會(huì)阻掛起當(dāng)前線程,而會(huì)立刻返回。
同步:同步和阻塞比較相似,但是二者并不是同一個(gè)概念,同步是指完成事件的邏輯,是指一件事完成之后,再完成第二件事,以此類推…
異步:異步和非阻塞比較類似,異步的概念和同步相對(duì)。當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者不能立刻得到結(jié)果。實(shí)際處理這個(gè)調(diào)用的部件在完成后,通過狀態(tài)、通知和回調(diào)來(lái)通知調(diào)用者,實(shí)現(xiàn)異步的方式通俗講就是“等會(huì)再告訴你”。
1)阻塞方式
回到代碼上,首先實(shí)現(xiàn)阻塞方式的請(qǐng)求函數(shù):
def blocking_way(): sock = socket.socket() sock.connect(('www.sina.com',80)) request = 'GET / HTTP/1.0/r/nHOST:www.sina.com/r/n/r/n' sock.send(request.encode('ascii')) response = b'' chunk = sock.recv(4096) while chunk: response += chunk chunk = sock.recv(4096) return response
測(cè)試線程、多進(jìn)程和多線程
# 阻塞無(wú)并發(fā)@tsfuncdef sync_way(): res = [] for i in range(10): res.append(blocking_way()) return len(res)@tsfunc# 阻塞、多進(jìn)程def process_way(): worker = 10 with futures.ProcessPoolExecutor(worker) as executor: futs = {executor.submit(blocking_way) for i in range(10)} return len([fut.result() for fut in futs])# 阻塞、多線程@tsfuncdef thread_way(): worker = 10 with futures.ThreadPoolExecutor(worker) as executor: futs = {executor.submit(blocking_way) for i in range(10)} return len([fut.result() for fut in futs])
新聞熱點(diǎn)
疑難解答
圖片精選