Tornado 4.0+ 大量採用 **協程** 特性,讓異步代碼呈現同步風格,擺脫回調地獄。本文從 Python 生成器機制出發,逐步構建簡易協程調度器,揭示 Tornado @gen.coroutine 與 yield 的底層運行邏輯。
Python 生成器通過 yield 關鍵字**暫停執行並保存狀態**,下次調用 next() 或 send() 可從暫停點恢復。
>> def test():
... yield 1
...
>>> t = test()
>>> t
>>> next(t)
1
>>> next(t)
StopIteration
yield 不僅可**產出值**,還可**接收外部傳入值**(賦值給變量):
>> def test():
... a = yield
... print(a)
...
>>> t = test()
>>> next(t) # 必須先 next() 啟動生成器
>>> t.send('hello') # 發送值並恢復執行
hello
StopIteration
兩個生成器協作:loop1 產生任務,loop2 執行任務,主循環調度:
def loop1():
a, ret = 0, 1
while True:
ret = yield sum, [a, ret] # 產出任務 (函數, 參數)
a, ret = ret, a
print("Loop1 ret", ret)
def loop2():
while True:
func, args = yield # 接收任務
yield func(*args) # 執行並產出結果
print("Loop2")
l1 = loop1()
l2 = loop2()
tmp = next(l1) # 啟動 loop1,獲取第一個任務
for i in range(10):
next(l2) # 啟動 loop2,準備接收任務
ret = l2.send(tmp) # 發送任務,獲得執行結果
tmp = l1.send(ret) # 將結果發回 loop1,獲取下一任務
這正是協程核心:**生成器作為可暫停/恢復的計算單元,調度器負責在生成器間傳遞數據與控制權。**
典型 Tornado 協程用法:
class ActionHandler(web.RequestHandler):
@gen.coroutine
def get(self):
response = yield httpclient.AsyncHTTPClient().fetch("https://www.example.com")
# 異步等待 HTTP 響應,不阻塞事件循環
# 收到響應後自動恢復執行此處後續代碼
對應上文模型:
ActionHandler.get 生成器 ≈ loop1(任務發起者)。yield httpclient.AsyncHTTPClient().fetch(...) 產出一個 Future 對象(任務描述)。send() 回生成器 → 生成器恢復執行後續邏輯。yield 暫停點,send() 恢復點。yield 交出控制權給 IOLoop,IOLoop 可處理其他連接/定時器/任務。try/except 可捕獲異步異常)。Tornado 協程實現涉及 gen.py、ioloop.py、futures.py 等核心模塊,封裝了異常處理、超時控制、多 yield 協調、巢狀協程等復雜邏輯。有興趣者強烈建議**閱讀源碼**,領略「在生成器之上構建異步框架」的工程智慧。