協程(微線程,Coroutine)。python
server的發展以下:git
IO密集型應用: 多進程->多線程->事件驅動->協程github
CPU密集型應用:多進程-->多線程編程
多進程對應多CPU,多線程對應多核CPU,事件驅動和協程則是充分挖掘單核能力。安全
異步事件驅動模型中,把會致使阻塞的操做轉化爲一個異步操做,主線程負責發起這個異步操做,並處理這個異步操做的結果。因爲全部阻塞的操做都轉化爲異步操做,理論上主線程的大部分時間都是在處理實際的計算任務,少了多線程的調度時間,因此這種模型的性能一般會比較好。總的說來,當單核cpu性能提高,cpu不在成爲性能瓶頸時,採用異步server可以簡化編程模型,也能提升IO密集型應用的性能。多線程
爲何要用協程?併發
效率,效率,效率,重要的事情說三遍!異步
爲何協程的效率高?異步編程
一個線程執行,沒有線程切換開銷;性能
由於只有一個線程,不須要多線程的鎖機制;
python對協程的支持是經過generator來實現的。
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
整個流程無鎖,由一個線程執行,produce
和consumer
協做完成任務,因此稱爲「協程」,而非線程的搶佔式多任務。
上面的例子中,整個過程沒有鎖的出現,還能保證數據安全,還能夠控制順序,優雅的實現了併發,甩多線程幾條街
線程叫微進程,而協程又叫微線程。協程擁有本身的寄存器上下文和棧,所以能保留上一次調用的狀態。
Python環境下使用協程通常調用是gevent模塊,其基本原理是:
當一個greenlet遇到IO操做時,就會自動切換到其餘的greenlet,等IO操做完成,再切換回來,這樣就保證了總有greenlet在運行,而不是等待。
import requests
import gevent
import time
def foo(url):
response=requests.get(url)
response_str=response.text
print('get data %s'%len(response_str))
s=time.time()
gevent.joinall([gevent.spawn(foo,"https://itk.org/"),
gevent.spawn(foo, "https://www.github.com/"),
gevent.spawn(foo, "https://zhihu.com/"),])
# foo("https://itk.org/")
# foo("https://www.github.com/")
# foo("https://zhihu.com/")
print(time.time()-s)
from gevent.pool import Pool
pool = Pool(2)
g1 = pool.spwan(get_page,'www.baidu.com')
g2 = pool.spwan(get_page,'www.baidu2.com')
g3 = pool.spwan(get_page,'www.baidu3.com')
gevent.joinall([g1,g3,g2,])
print(g1.value,g2.value)
進程是具備必定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每一個進程都有本身的獨立內存空間,不一樣進程經過進程間通訊來通訊。因爲進程比較重量,佔據獨立的內存,因此上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程本身基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),可是它可與同屬一個進程的其餘的線程共享進程所擁有的所有資源。線程間通訊主要經過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。
協程是一種用戶態的輕量級線程,協程的調度徹底由用戶控制。協程擁有本身的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,直接操做棧則基本沒有內核切換的開銷,能夠不加鎖的訪問全局變量,因此上下文的切換很是快。
區別:
線程是指進程內的一個執行單元,也是進程內的可調度實體。線程與進程的區別:
1) 地址空間:線程是進程內的一個執行單元,進程內至少有一個線程,它們共享進程的地址空間,而進程有本身獨立的地址空間
2) 資源擁有:進程是資源分配和擁有的單位,同一個進程內的線程共享進程的資源
3) 線程是處理器調度的基本單位,但進程不是
4) 兩者都可併發執行
5) 每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口,可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制
1) 一個線程能夠多個協程,一個進程也能夠單獨擁有多個協程,這樣python中則能使用多核CPU。
2) 線程進程都是同步機制,而協程則是異步
3) 協程能保留上一次調用時的狀態,每次過程重入時,就至關於進入上一次調用的狀態
全部的改變都是有目的的!
一開始爲了執行多任務,出現了併發,能夠是多CPU並行,也能夠是單CPU時間分片;
多任務會涉及到一個問題:切換。切換涉及到上下文,狀態保存;爲此,有了進程;
多進程對應多CPU,即並行;
多任務的基本面是處理能力遠遠超出需求,若是一個CPU只能知足基本使用要求,不會有多任務需求的出現;
那麼新的問題是,CPU空跑,處理比I/O快,所以有了I/O阻塞,類似的還有時鐘阻塞;
爲解決CPU空跑,就要切換任務,進程切換開銷比較高;怎麼辦,線程;
(固然減小人的等待時間也是多任務的目的,可是處理方法是相同的,多任務+切換任務)
在特定狀況下切換線程仍是開銷過高。。。
若是時鐘阻塞,線程切換功能不須要,在進程裏寫一個邏輯流調度,既能夠利用到併發,又能夠避免
反覆系統調用,還有進程切換的開銷,這就是用戶態線程,分分鐘給你上千個邏輯流;
從上面能夠看到,實現一個用戶態線程有兩個必須處理的問題:一是阻塞式I/O,二是因爲缺少時鐘阻塞,
進程須要本身有調度線程的能力。
若是一種實現使得每一個線程須要本身經過調用某個方法,主動交出控制權。那麼咱們就稱這種用戶態線程
是協做式的,即協程。
本質上協程就是用戶空間下的線程。
固然改變通常都是有代價的,上述每一步發展過程都有意味着通用性下降和穩定性下降。
異步編程:
IO模型:
進程,線程:
python yield語句: