## 什么是協程
協程,又稱微線程,纖程。英文名Coroutine。**協程是一種用戶態的輕量級線程**。協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:協程能保留上一次調用時的狀態,每次過程重入時,就相當于進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。
### 協程優點
* 無需線程上下文切換的開銷
* 無需原子操作鎖定及同步的開銷
* 方便切換控制流,簡化編程模型
* 高并發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用于高并發處理。
PS: "原子操作(atomic operation)是不需要synchronized",所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執行部分。視作整體是原子性的核心。
### 協程缺點
* 無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
* 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
## 協程引入
之前我們有用過yield關鍵字將用過函數變成一個生成器,實現了在單線程情況下的并發效果,如下例所示:
```
import time
import queue
def consumer(name):
print("--->starting eating baozi...")
while True:
new_baozi = yield
print("[%s] is eating baozi %s" % (name,new_baozi))
time.sleep(1)
def producer():
r = con.__next__()
r = con2.__next__()
n = 0
while n < 5:
n +=1
con.send(n)
con2.send(n)
print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
if __name__ == '__main__':
con = consumer("c1")
con2 = consumer("c2")
p = producer()
```
那么這算不算協程呢?我們先要知道到底滿足什么條件才算是一個協程。
1. **必須在只有一個單線程里實現并發**
2. **修改共享數據不需加鎖**
3. **用戶程序里自己保存多個控制流的上下文棧**
4. **一個協程遇到IO操作自動切換到其它協程**
如果滿足了以上的條件我們就可以認為是一個協程。所以yield并完全算是一個協程,因為它不能實現遇到IO操作就自動切換到其他協程的功能。
## Greenlet
greenlet是一個用C實現的協程模塊,相比與python自帶的yield,它可以使你在任意函數之間隨意切換,而不需把這個函數先聲明為generator。
~~~
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
gr2.switch()
def test2():
print(56)
gr1.switch()
print(78)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
執行結果:
12
56
34
78
~~~
但是這個模塊沒有解決一個問題,就是自動切換IO操作,上面的切換是我們人為手動切換的。
## Gevent
Gevent 是一個第三方庫,可以輕松通過gevent實現并發同步或異步編程,在gevent中用到的主要模式是**Greenlet**, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。
~~~
import gevent
def func1():
print('\033[31;1m我是func1的1\033[0m')
gevent.sleep(2)
print('\033[31;1m我是func1的2\033[0m')
def func2():
print('\033[32;1m我是func2的1\033[0m')
gevent.sleep(1)
print('\033[32;1m我是func2的2\033[0m')
def func3():
print('\033[32;1m我是func3的1\033[0m')
gevent.sleep(0)
print('\033[32;1m我是func3的2\033[0m')
gevent.joinall([
gevent.spawn(func1),
gevent.spawn(func2),
gevent.spawn(func3),
])
輸出結果:
我是func1的1
我是func2的1
我是func3的1
我是func3的2
我是func2的2
我是func1的2
~~~
- Python學習
- Python基礎
- Python初識
- 列表生成式,生成器,可迭代對象,迭代器詳解
- Python面向對象
- Python中的單例模式
- Python變量作用域、LEGB、閉包
- Python異常處理
- Python操作正則
- Python中的賦值與深淺拷貝
- Python自定義CLI三方庫
- Python并發編程
- Python之進程
- Python之線程
- Python之協程
- Python并發編程與IO模型
- Python網絡編程
- Python之socket網絡編程
- Django學習
- 反向解析
- Cookie和Session操作
- 文件上傳
- 緩存的配置和使用
- 信號
- FBV&&CBV&&中間件
- Django補充
- 用戶認證
- 分頁
- 自定義搜索組件
- Celery
- 搭建sentry平臺監控
- DRF學習
- drf概述
- Flask學習
- 項目拆分
- 三方模塊使用
- 爬蟲學習
- Http和Https區別
- 請求相關庫
- 解析相關庫
- 常見面試題
- 面試題
- 面試題解析
- 網絡原理
- 計算機網絡知識簡單介紹
- 詳解TCP三次握手、四次揮手及11種狀態
- 消息隊列和數據庫
- 消息隊列之RabbitMQ
- 數據庫之Redis
- 數據庫之初識MySQL
- 數據庫之MySQL進階
- 數據庫之MySQL補充
- 數據庫之Python操作MySQL
- Kafka常用命令
- Linux學習
- Linux基礎命令
- Git
- Git介紹
- Git基本配置及理論
- Git常用命令
- Docker
- Docker基本使用
- Docker常用命令
- Docker容器數據卷
- Dockerfile
- Docker網絡原理
- docker-compose
- Docker Swarm
- HTML
- CSS
- JS
- VUE