程序員如何在一個線程的情況下實現併發?

python相對於Java有個新的概念,那就是“協程”。協程是多個任務同時處理的協做機制,用來處理IO等待阻塞。當然Go裡面也有goroutine,但和python的coroutine還是有本質的區別的。goroutine是一個調度模型,模型由內核級線程、語言級線程、隊列組成的。協程代碼如下:

程序員如何在一個線程的情況下實現併發?

耗時

程序員如何在一個線程的情況下實現併發?

源碼

import gevent
from gevent import monkey
from gevent import pool as event_pool
monkey.patch_all()
import time
import urllib3
from lxml import html

urllib3.disable_warnings()
pool = event_pool.Pool(1000)
http = urllib3.PoolManager(retries=3, timeout=30, num_pools=200, maxsize=200)


def download(image_url):
response = http.request("GET", image_url)
ht = html.fromstring(response.data.decode())
title = ht.xpath('//title/text()')
print('image_url: ', image_url, ', ', 'title: ', title, '\\n')
return title


def normal():
start = time.time()
download('https://www.baidu.com/')
download('https://www.sogou.com/')
download('https://www.so.com/')
end = time.time()
print('normal cost: ', (end - start), '\\n')


def coroutine():
start = time.time()
tasks = [pool.spawn(download, 'https://www.baidu.com/'),
pool.spawn(download, 'https://www.sogou.com/'),
pool.spawn(download, 'https://www.so.com/')]
for task in tasks:
task.add_done_callback(result)
gevent.joinall(tasks)
end = time.time()
print('coroutine cost: ', (end - start), '\\n')


def result(res):
print('result: ', res)



if __name__ == '__main__':
normal()
coroutine()

原理

python協程歸結於底層關鍵字yield,在一個線程中手動切換任務。如果有耗時IO則交出執行權,不等待IO,CPU跑起來。線程調度一般都是計算機或虛擬機底層系統自動調度的,協程則是手動控制的。協程信息一般保留在當前線程棧,這樣做可以降低系統線程切換帶來的性能損耗,提高運行的效率。greenlet、async都是自己指定何時切換協程,gevent自動在遇到IO阻塞時切換。

程序員如何在一個線程的情況下實現併發?

由此可見,程序在“協程”模式下比正常的順序執行耗時明顯減少,一個線程也可以處理併發~



分享到:


相關文章: