Lua基礎之coroutine(協程)

概括:1.創建協程2.coroutine的函數3.coroutine的基本流程4.yield對coroutine流程的干預5.resume, function()以及yield之間的參數傳遞和返回值傳遞

原文地址:http://blog.csdn.net/dingkun520wy/article/details/50212199


1.創建協程

協程和多線程下的線程類似:有自己的堆棧,自己的局部變量,有自己的指令指針,但是和其他協程程序共享全局變量等信息。線程和協程的主要不同在於:多處理器的情況下,概念上來說多線程是同時運行多個線程,而協程是通過協作來完成,任何時刻只有一個協程程序在運行。並且這個在運行的協程只有明確被要求掛起時纔會被掛起。

--創建協程
co = coroutine.create(function ()
     print("hi")
end)
--啓動協程
coroutine.resume(co)

2.coroutine的函數

(1) coroutine.create()  創建協程

(2) coroutine.resume() 運行協程,可以向協程內傳遞參數

(3) coroutine.yield() 掛起協程,可以向外傳遞參數

(4) coroutine.status() 返回協程當前狀態,coroutine的狀態分爲suspend, running, dead三種。


3.coroutine的基本流程

co = coroutine.create(function(a, b)
    print(coroutine.status(co), "start")   --執行代碼,coroutine狀態爲running--->(3)
    print("co", a, b)
    print(coroutine.status(co), "end")    --執行代碼,coroutine狀態爲running     --->(4)
end)

print(coroutine.status(co))       --剛創建的coroutine的狀態爲suspend   --->(1)
coroutine.resume(co, 1, 2)       --啓動coroutine,將跳轉到coroutine的function執行   --->(2)
print(coroutine.status(co))       --coroutine執行完畢,狀態爲dead    --->(5)

代碼的執行結果如下:

suspended
running    start
co    1    2
running    end
dead


4.yield對coroutine流程的干預

yield作用是將一個running的coroutine掛起,相應的其狀態就會被切換成suspend。在執行到yield之後,代碼跳轉到上一次resume代碼的後一條代碼執行,再次調用resume,代碼就跳轉到上一次yield代碼的後一條代碼執行。一般來說,resume方法在主線程中調用;而yield則是coroutine內調用,包括coroutine內部調用的函數內部。在coroutine中調用resume沒有什麼問題,但這樣是沒有什麼意義的,因爲如果代碼還在coroutine中執行的話,則說明其狀態一定是running的,這個時候的resume是沒有任何意義的。而在主線程中調用yield,會導致 “lua: attempt to yield across metamethod/C-call boundary”的錯誤。

co = coroutine.create(function(a, b)
	print(coroutine.status(co), "start")				--->(2)
	for i = 1, 10 do
		print("co", a, b)								--->(3)(6)
		coroutine.yield()
		print(coroutine.status(co), "after yield")		--->(5)
	end
	print(coroutine.status(co), "end")
end)

print(coroutine.status(co))								--->(1)
coroutine.resume(co, 1, 2)
print(coroutine.status(co))								--->(4)
coroutine.resume(co, 1, 2)								
print(coroutine.status(co))								--->(7)

執行結果

suspended
running start
co 1 2
suspended
running after yield
co 1 2
suspended


5.resume, function()以及yield之間的參數傳遞和返回值傳遞

resume的參數除了coroutine句柄(第一個參數)以外,都傳遞給了function,關係跟表達式賦值是一致的,少的以nil補足,多的捨棄。

co1 = coroutine.create(function(a, b)
	print("co", a, b)
end)

co2 = coroutine.create(function(a, b)
	print("co", a, b)
end)

co3 = coroutine.create(function(a, b)
	print("co", a, b)
end)

coroutine.resume(co1, 1)
coroutine.resume(co2, 1, 2)
coroutine.resume(co3, 1, 2, 3)


執行結果如下:

co1nil
co 1 2
co 1 2

如果在coroutine中包含有yield,情況會複雜一些。

我們進一步挖掘coroutine的流程:

1 resume 

2 function

3 yield

4 yield掛起,第一次 resume返回

5 第二次resume

6 yield返回

7 function 繼續執行

在這個流程的第一步的時候,resume的參數會傳遞給function,作爲參數(具體如上);到了第三步的時候,yield的參數會作爲resume返回值的一部分;而第二次resume(第五步)的時候,resume的參數的作用發生了改變,resume的參數會傳遞給yield,做爲yield的返回值。

這個過程很精巧,在coroutine執行的過程中返回,必然需要告訴外部現在coroutine這個時候的內部的的情況,通過唯一的接口yield的參數作爲resume的返回值,高;到了第二次resume的時候,外部的環境必然發生了改變, 怎麼通知coroutine內部呢,同樣的想法,將唯一的接口resume的參數通過yield的返回的途徑返回到coroutine內部,一樣的高明。

貼一個引用的代碼

function foo (a)
    print("foo", a)  -- foo 2
    return coroutine.yield(2 * a) -- return: a , b
end
 
co = coroutine.create(function (a , b)
    print("co-body", a, b) -- co-body 1 10
    local r = foo(a + 1)
     
    print("co-body2", r)
    local r, s = coroutine.yield(a + b, a - b)
     
    print("co-body3", r, s)
    return b, "end"
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")) -- false cannot resume dead coroutine
print("------")

結果如下:

co-body 1 10
foo 2
main true 4
------
co-body2 r
main true 11 -9
------
co-body3 x y
main true 10 end

------
main false cannot resume dead coroutine
------


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章