【笨木頭Lua專欄】基礎補充07:協同程序初探

哎。週五晚上我都還這麼努力看書。真是好孩子。(小若:不想吐槽了)php

事實上我都準備玩遊戲看電影去的了。但是這書就擺在桌子上。而且正對着我,就想着。掃兩眼吧。多線程

結果一掃就不正確勁了,因爲這內容有點繞,有點小混亂,假設我現在不記錄下來的話。下週一可能又要又一次看一次了。ide

 

好吧,今天咱們來聊聊協同程序。函數

 

笨木頭花心貢獻,哈?花心?不,是用心~post

轉載請註明,原文地址: http://www.benmutou.com/archives/1733ui

文章來源:笨木頭與遊戲開發lua

 

1.什麼是協同程序(coroutine)

你們都知道線程吧?都知道多線程吧?協同程序就和這線程差點兒相同,但是又有比較明顯的區別。url

多個協同程序在隨意時刻僅僅能運行一個,儘管線程在某種意義上也是這樣,但這不是同樣的概念。spa

換句話說,一個協同程序在運行的時候。其它協同程序是沒法得到運行的機會的。線程

僅僅有正在運行的協同程序主動掛起時,其它協同程序纔有機會運行。

 

而線程呢?即便不主動休眠。也很是有可能因爲輪片時間到達而把運行機會讓給其它線程。

 

2.建立協同程序

建立協同程序很是easy。咋一看。事實上和線程沒區別~

代碼例如如下:

  
  
  
  
  1.     local co = coroutine.create(function() print("hello coroutine"); end);

協同的程序的操做都在coroutine裏,create函數的參數就是協同程序要運行的函數,就這麼運行代碼是沒有效果的。

因爲協同程序建立後,默認是掛起狀態。

協同程序的四種狀態分別爲:掛起(suspended)、運行(running)、死亡(dead)、正常(normal)。

 

要想協同程序運行起來,就要調用resume函數。

例如如下代碼:

  
  
  
  
  1.     local co = coroutine.create(function() print("hello coroutine"); end);
  2.     coroutine.resume(co);

輸出結果例如如下:

[LUA-print] hello coroutine

 

3.更像樣的協同程序

剛剛那個協同程序太簡陋的。沒有不論什麼做用,直接打印一條語句以後就結束了,同一時候它的狀態也變成了死亡狀態。

咱們來一個帥一點的協同程序:

  
  
  
  
  1.     local co = coroutine.create(function()
  2.         for i = 1, 2, 1 do
  3.             print("木頭挺聰明的+" .. i);
  4.         end
  5.     end);
  6.     coroutine.resume(co);

運行結果例如如下:

[LUA-print] 木頭挺聰明的+1
[LUA-print] 木頭挺聰明的+2

因此我就說,電腦就是誠實。這日誌打印的,真好看(小若:咱們不要理這個神經病了)

 

4.讓協同程序掛起——yield

既然協同程序和線程差點兒相同。那確定不能讓協同程序一次過運行完成了,這就沒有意義了。

咱們來看看怎麼讓協同程序掛起,例如如下代碼:

  
  
  
  
  1.     local co = coroutine.create(function()
  2.         for i = 1, 2, 1 do
  3.             print("木頭挺聰明的+" .. i);
  4.             coroutine.yield();
  5.         end
  6.     end);
  7.     coroutine.resume(co);
  8.     print(coroutine.status(co));

輸出結果例如如下:

[LUA-print] 木頭挺聰明的+1
[LUA-print] suspended

這回就僅僅輸出了一條日誌就中止了,後面咱們還調用了status函數,打印協同程序當前的狀態,suspended即爲掛起狀態。

因爲這個協同程序尚未運行完成。因此僅僅能是掛起狀態。

 

那麼,假設讓這協同程序繼續運行呢?很是easy,再次調用resume函數。如代碼:

  
  
  
  
  1.     local co = coroutine.create(function()
  2.         for i = 1, 2, 1 do
  3.             print("木頭挺聰明的+" .. i);
  4.             coroutine.yield();
  5.         end
  6.     end);
  7.     coroutine.resume(co);
  8.     print(coroutine.status(co));
  9.    
  10.     coroutine.resume(co);
  11.     print(coroutine.status(co));
  12.    
  13.     coroutine.resume(co);
  14.     print(coroutine.status(co));

此次有點複雜了。先看看輸出結果:

[LUA-print] 木頭挺聰明的+1
[LUA-print] suspended
[LUA-print] 木頭挺聰明的+2
[LUA-print] suspended
[LUA-print] dead

我一共運行了三次resume函數。但很是顯然,這個協同程序的for循環僅僅會運行2次。

那爲何第二次resume運行以後,協同程序的狀態仍是掛起呢?不該該是結束了麼?結束了就應該是死亡狀態了。

而第三次運行resume以後,反而沒有不論什麼輸出,此時的狀態才真正切換到死亡狀態。

 

這是爲何呢?(小若:趕忙說,不說我看電影去了)

再來這麼看看就明確了。加幾條打印代碼:

  
  
  
  
  1.     local co = coroutine.create(function()
  2.         for i = 1, 2, 1 do
  3.             print("木頭挺聰明的+" .. i);
  4.             coroutine.yield();
  5.             print("一次循環結束");
  6.         end
  7.         print("協同程序結束");
  8.     end);
  9.     coroutine.resume(co);
  10.     print(coroutine.status(co));
  11.    
  12.     coroutine.resume(co);
  13.     print(coroutine.status(co));
  14.    
  15.     coroutine.resume(co);
  16.     print(coroutine.status(co));

輸出結果例如如下:

[LUA-print] 木頭挺聰明的+1
[LUA-print] suspended
[LUA-print] 一次循環結束
[LUA-print] 木頭挺聰明的+2
[LUA-print] suspended
[LUA-print] 一次循環結束
[LUA-print] 協同程序結束
[LUA-print] dead

這就很是明顯了,在協同程序裏調用yield函數時。會被掛起,而yield函數的返回要等下一次調用resume函數時才幹獲得。

因此,yield函數如下的print語句在下一次的resume調用時才被運行。

又因此,當for循環第二次運行時,協同程序被掛起,需要等待再一次resume時。for循環才幹真正運行完成。

這就是這段代碼的特殊之處了。

 

5.resume操做的返回值

事實上resume函數是有返回值的。

咱們試試運行如下的代碼:

  
  
  
  
  1.     local co = coroutine.create(function()
  2.         for i = 1, 2, 1 do
  3.             coroutine.yield();
  4.         end
  5.     end);
  6.     local result, msg = coroutine.resume(co);
  7.     print(result);
  8.     print(msg);

輸出結果例如如下:

[LUA-print] true
[LUA-print] nil

resume返回兩個值,第一個值表明協同程序是否正常運行。第二個返回值天然是表明錯誤信息。

咱們試試讓協同程序出現錯誤:

  
  
  
  
  1.     local co = coroutine.create(function()
  2.         error("呵呵,報錯了吧");
  3.     end);
  4.     local result, msg = coroutine.resume(co);
  5.     print(result);
  6.     print(msg);

輸出結果例如如下:

[LUA-print] false
[LUA-print] [string "src/main.lua"]:91: 呵呵。報錯了吧

 

6.結束

好了,儘管我已經寫了這麼多了,但是我真正想記錄的東西還沒開始寫呢~!

我了個噗,今晚我還能不能好好玩了…

好吧,內容有點多,下一篇繼續…

相關文章
相關標籤/搜索