# Lua 協同程序(coroutine)
### 什么是協同(coroutine)?
Lua 協同程序(coroutine)與線程比較類似:擁有獨立的堆棧,獨立的局部變量,獨立的指令指針,同時又與其它協同程序共享全局變量和其它大部分東西。
協同是非常強大的功能,但是用起來也很復雜。
### 線程和協同程序區別
線程與協同程序的主要區別在于,一個具有多個線程的程序可以同時運行幾個線程,而協同程序卻需要彼此協作的運行。
在任一指定時刻只有一個協同程序在運行,并且這個正在運行的協同程序只有在明確的被要求掛起的時候才會被掛起。
協同程序有點類似同步的多線程,在等待同一個線程鎖的幾個線程有點類似協同。
### 基本語法
| 方法 | 描述 |
|-----|-----|
| coroutine.create() | 創建coroutine,返回coroutine, 參數是一個函數,當和resume配合使用的時候就喚醒函數調用 |
| coroutine.resume() | 重啟coroutine,和create配合使用 |
| coroutine.yield() | 掛起coroutine,將coroutine設置為掛起狀態,這個和resume配合使用能有很多有用的效果 |
| coroutine.status() | 查看coroutine的狀態 注:coroutine的狀態有三種:dead,suspend,running,具體什么時候有這樣的狀態請參考下面的程序 |
| coroutine.wrap() | 創建coroutine,返回一個函數,一旦你調用這個函數,就進入coroutine,和create功能重復 |
| coroutine.running() | 返回正在跑的coroutine,一個coroutine就是一個線程,當使用running的時候,就是返回一個corouting的線程號 |
### 以下實例演示了以上各個方法的用法:
~~~
~~~
--?coroutine_test.lua?文件
co?=?coroutine.create(
????function(i)
????????print(i);
????end
)
coroutine.resume(co,?1)???--?1
print(coroutine.status(co))??--?dead
print("----------")
co?=?coroutine.wrap(
????function(i)
????????print(i);
????end
)
co(1)
print("----------")
co2?=?coroutine.create(
????function()
????????for?i=1,10?do
????????????print(i)
????????????if?i?==?3?then
????????????????print(coroutine.status(co2))??--running
????????????????print(coroutine.running())?--thread:XXXXXX
????????????end
????????????coroutine.yield()
????????end
????end
)
coroutine.resume(co2)?--1
coroutine.resume(co2)?--2
coroutine.resume(co2)?--3
print(coroutine.status(co2))???--?suspended
print(coroutine.running())???--nil
print("----------")
~~~
~~~
以上實例執行輸出結果為:
~~~
~~~
1
dead
----------
1
----------
1
2
3
running
thread:?0x7fb801c05868??false
suspended
thread:?0x7fb801c04c88??true
----------
~~~
~~~
coroutine.running就可以看出來,coroutine在底層實現就是一個線程。
當create一個coroutine的時候就是在新線程中注冊了一個事件。
當使用resume觸發事件的時候,create的coroutine函數就被執行了,當遇到yield的時候就代表掛起當前線程,等候再次resume觸發事件。
接下來我們分析一個更詳細的實例:
~~~
~~~
function?foo?(a)
????print("foo?函數輸出",?a)
????return?coroutine.yield(2?*?a)?--?返回??2*a?的值
end
co?=?coroutine.create(function?(a?,?b)
????print("第一次協同程序執行輸出",?a,?b)?--?co-body?1?10
????local?r?=?foo(a?+?1)
????print("第二次協同程序執行輸出",?r)
????local?r,?s?=?coroutine.yield(a?+?b,?a?-?b)??--?a,b的值為第一次調用協同程序時傳入
????print("第三次協同程序執行輸出",?r,?s)
????return?b,?"結束協同程序"???????????????????--?b的值為第二次調用協同程序時傳入
end)
print("main",?coroutine.resume(co,?1,?10))?--?true,?4
print("--分割線----")
print("main",?coroutine.resume(co,?"r"))?--?true?11?-9
print("---分割線---")
print("main",?coroutine.resume(co,?"x",?"y"))?--?true?10?end
print("---分割線---")
print("main",?coroutine.resume(co,?"x",?"y"))?--?cannot?resume?dead?coroutine
print("---分割線---")
~~~
~~~
以上實例執行輸出結果為:
~~~
~~~
第一次協同程序執行輸出?1???10
foo?函數輸出????2
main????true????4
--分割線----
第二次協同程序執行輸出?r
main????true????11??-9
---分割線---
第三次協同程序執行輸出?x???y
main????true????10??結束協同程序
---分割線---
main????false???cannot?resume?dead?coroutine
---分割線---
~~~
~~~
以上實例接下如下:
- 調用resume,將協同程序喚醒,resume操作成功返回true,否則返回false;
- 協同程序運行;
- 運行到yield語句;
- yield掛起協同程序,第一次resume返回;(注意:此處yield返回,參數是resume的參數)
- 第二次resume,再次喚醒協同程序;(注意:此處resume的參數中,除了第一個參數,剩下的參數將作為yield的參數)
- yield返回;
- 協同程序繼續運行;
- 如果使用的協同程序繼續運行完成后繼續調用 resumev方法則輸出:cannot resume dead coroutine
resume和yield的配合強大之處在于,resume處于主程中,它將外部狀態(數據)傳入到協同程序內部;而yield則將內部的狀態(數據)返回到主程中。
### 生產者-消費者問題
現在我就使用Lua的協同程序來完成生產者-消費者這一經典問題。
~~~
~~~
local?newProductor
function?productor()
?????local?i?=?0
?????while?true?do
??????????i?=?i?+?1
??????????send(i)?????--?將生產的物品發送給消費者
?????end
end
function?consumer()
?????while?true?do
??????????local?i?=?receive()?????--?從生產者那里得到物品
??????????print(i)
?????end
end
function?receive()
?????local?status,?value?=?coroutine.resume(newProductor)
?????return?value
end
function?send(x)
?????coroutine.yield(x)?????--?x表示需要發送的值,值返回以后,就掛起該協同程序
end
--?啟動程序
newProductor?=?coroutine.create(productor)
consumer()
~~~
~~~
~~~
~~~
local?newProductor
function?productor()
?????local?i?=?0
?????while?true?do
??????????i?=?i?+?1
??????????send(i)?????--?將生產的物品發送給消費者
?????end
end
function?consumer()
?????while?true?do
??????????local?i?=?receive()?????--?從生產者那里得到物品
??????????print(i)
?????end
end
function?receive()
?????local?status,?value?=?coroutine.resume(newProductor)
?????return?value
end
function?send(x)
?????coroutine.yield(x)?????--?x表示需要發送的值,值返回以后,就掛起該協同程序
end
--?啟動程序
newProductor?=?coroutine.create(productor)
consumer()
~~~
~~~
以上實例執行輸出結果為:
~~~
~~~
1
2
3
4
5
6
7
8
9
10
11
12
13
……
~~~
~~~
- Lua 簡介
- Lua 環境安裝
- Lua 數據類型
- Lua 變量
- Lua 循環
- Lua while 循環
- Lua for 循環
- Lua repeat...until 循環
- Lua 循環嵌套
- Lua break 語句
- Lua 流程控制
- Lua if 語句
- Lua if...else 語句
- Lua if 嵌套語句
- Lua 函數
- Lua 運算符
- Lua 字符串
- Lua 數組
- Lua 迭代器
- Lua table(表)
- Lua 模塊與包
- Lua 元表(Metatable)
- Lua 協同程序(coroutine)
- Lua 文件 I/O
- Lua 錯誤處理
- Lua 調試(Debug)
- Lua 垃圾回收
- Lua 面向對象
- Lua 數據庫訪問
- CoverPage