Lua的執行緒和狀態

2022-07-01 20:57:08 字數 4847 閱讀 6017

【那不是真的多執行緒】

lua不支援真正的多執行緒,這句話我在《lua中的協同程式》這篇文章中就已經說了。根據我的程式設計經驗,在開發過程中,如果可以避免使用執行緒,那就堅決不用執行緒,如果實在沒有更好的辦法,那就只能退而用之。為什麼?首先,多個執行緒之間的通訊比較麻煩,同時,執行緒之間共享記憶體,對於共享資源的訪問,使用都是乙個不好控制的問題;其次,執行緒之間來回切換,也會導致一些不可預估的問題,對效能也是一種損耗。lua不支援真正的多執行緒,而是一種協作式的多執行緒,彼此之間協作完成,並不是搶占完成任務,由於這種協作式的執行緒,因此可以避免由不可預知的執行緒切換所帶來的問題;另一方面,lua的多個狀態之間不共享記憶體,這樣便為lua中的併發操作提供了良好的基礎。

【多個執行緒】

從c api的角度來看,將執行緒想象成乙個棧可能更形象些。從實現的觀點來看,乙個執行緒的確就是乙個棧。每個棧都保留著乙個執行緒中所有未完成的函式呼叫資訊,這些資訊包括呼叫的函式、每個呼叫的引數和區域性變數。也就是說,乙個棧擁有乙個執行緒得以繼續執行的所有資訊。因此,多個執行緒就意味著多個獨立的棧。

當呼叫lua c api中的大多數函式時,這些函式都作用於某個特定的棧。當我們呼叫lua_pushnumber時,就會將數字壓入乙個棧中,那麼lua是如何知道該使用哪個棧的呢?答案就在型別lua_state中。這些c api的第乙個引數不僅表示了乙個lua狀態,還表示了乙個記錄在該狀態中的執行緒。

只要建立乙個lua狀態,lua就會自動在這個狀態中建立乙個新執行緒,這個執行緒稱為「主線程」。主線程永遠不會被**。當呼叫lua_close關閉狀態時,它會隨著狀態一起釋放。呼叫lua_newthread便可以在乙個狀態中建立其他的執行緒。

lua_state *lua_newthread(lua_state *l);

這個函式返回乙個lua_state指標,表示新建的執行緒。它會將新執行緒作為乙個型別為「thread」的值壓入棧中。如果我們執行了:

l1 = lua_newthread(l);

現在,我們擁有了兩個執行緒l和l1,它們內部都引用了相同的lua狀態。每個執行緒都有其自己的棧。新執行緒l1以乙個空棧開始執行,老執行緒l的棧頂就是這個新執行緒。

除了主線程以外,其它執行緒和其它lua物件一樣都是垃圾**的物件。當新建乙個執行緒時,執行緒會壓入棧,這樣能確保新執行緒不會成為垃圾,而有的時候,你在處理棧中資料時,不經意間就把線程彈出棧了,而當你再次使用該執行緒時,可能導致找不到對應的執行緒而程式崩潰。為了避免這種情況的發生,可以保持乙個對執行緒的引用,比如在登錄檔中儲存乙個對執行緒的引用。

當擁有了乙個執行緒以後,我們就可以像主線程那樣來使用它,以前博文中提到的對棧的操作,對這個新的執行緒都適用。然而,使用多執行緒的目的不是為了實現這些簡單的功能,而是為了實現協同程式。

為了掛起某些協同程式的執行,並在稍後恢復執行,我們可以使用lua_resume函式來實現。

int lua_resume(lua_state *l, int narg);

lua_resume可以啟動乙個協同程式,它的用法就像lua_call一樣。將待呼叫的函式壓入棧中,並壓入其引數,最後在呼叫lua_resume時傳入引數的數量narg。這個行為與lua_pcall類似,但有3點不同。

lua_resume沒有引數用於指出期望的結果數量,它總是返回被呼叫函式的所有結果;

它沒有用於指定錯誤處理函式的引數,發生錯誤時不會展開棧,這就可以在發生錯誤後檢查棧中的情況;

如果正在執行的函式交出(yield)了控制權,lua_resume就會返回乙個特殊的**lua_yield,並將執行緒置於乙個可以被再次恢復執行的狀態。

當lua_resume返回lua_yield時,執行緒的棧中只能看到交出控制權時所傳遞的那些值。呼叫lua_gettop則會返回這些值的數量。若要將這些值移到另乙個執行緒,可以使用lua_xmove。

現在,我就通過乙個簡單的程式來做個試驗,以便更好的理解lua的執行緒。使用c**來呼叫lua指令碼,lua函式作為乙個協同程式來啟動,這個lua函式可以呼叫其它lua函式,任意的乙個lua函式都可以交出控制權,從而使lua_resume呼叫返回。對於使用c呼叫lua不熟悉的夥計,請再去仔細的讀讀《lua與c》和《c「控制」lua》這兩篇文章吧。先貼上重要的**吧。下面是lua**:

function

func1(param1)

func2(param1 + 10

)

print("

func1 ended.")

return

30end

function

func2(value)

coroutine.yield(10

, value)

print("

func2 ended.")

end

下面是c++**:

lua_state *l1 =lua_newthread(l);

if (!l1)

lua_getglobal(l1,

"func1");

lua_pushinteger(l1,

10);

//執行這個協同程式

//這裡返回lua_yield

bret = lua_resume(l1, 1

);cout

<< "

bret:

"<< bret

cout << "

element num:

"<< lua_gettop(l1)

cout << "

value 1:

"<< lua_tointeger(l1, -2)

<< "

value 2:

"<< lua_tointeger(l1, -1)

//這裡返回0

bret = lua_resume(l1, 0

);cout

<< "

bret:

"<< bret

<< "

element num:

"<< lua_gettop(l1)

<< "

value 1:

"<< lua_tointeger(l1, -1) << endl;

上面的例子是c語言呼叫lua**,lua可以自己掛起自己;如果lua去呼叫c**呢?c函式不能自己掛起它自己,乙個c函式只有在返回時,才會交出控制權。因此c函式實際上是不會停止自身執行的,不過它的呼叫者可以是乙個lua函式,那麼這個c函式呼叫lua_yield,就可以掛起lua呼叫者:

int lua_yield(lua_state *l, int nresults);

你沒有聽錯,c**呼叫lua_yield不能掛起自己,但是它卻可以將它的lua呼叫者掛起。其中nresults是準備返回給相應resume的棧頂值的個數,當協同程式再次恢復執行時,lua呼叫者會收到傳遞給resume的值。lua_yield在使用時,只能作為乙個返回的表示式,而不能獨自使用。比如:

return lua_yield(l, 0);

對於多執行緒程式設計,本身就是麻煩的問題,而這裡枯燥的文字總結,也會沒有效果,下面來乙個簡短的例子。先貼lua**,這段**需要結合c**一起看,否則就是雲裡霧裡的。

require

"lua_yielddemo

"local function1 = function

()

local

value

repeat

value =module.func1()

until

value

return

value

endlocal thread1 = coroutine.create

(function1)

--現在執行到了module.func1()

--100這個值將會被賦值給value

coroutine.resume

(thread1)

--print(coroutine.status(thread1))

--設定c函式環境

module.func2(10

)print(coroutine.resume(thread1))

c**如下:

//

判斷環境表中jellythink是否被設定了

static

int isset(lua_state *l)

return1;

}static

int func1(lua_state *l)

//被設定了,就取值,返回被設定的值

printf("

resumed again\n");

lua_getfield(l, lua_environindex,

"jellythink");

return1;

}//設定jellthink的值

static

int func2(lua_state *l)

【lua狀態】

每次呼叫lual_newstate(或者lua_newstate)都會建立乙個新的lua狀態。不同的lua狀態是各自完全獨立的,它們之間不共享任何資料。這個概念是不是很熟悉,是不是特別像windows中的程序的概念。也就是說,在乙個lua狀態中發生的錯誤也不會影響其它的的lua狀態,windows的程序也是這樣的。並且,lua狀態之間不能直接溝通,必須寫一些輔助**來完成這點。

由於所有交換的資料必須經由c**中轉,所以只能在lua狀態間交換那些可以在c語言中表示的型別,例如字串和數字。由於lua狀態我目前沒有使用過,也就沒有足夠的信心和資格去總結這個東西,還是怕會誤導大家,如果以後在實際專案中使用了lua狀態,我還會回過頭來總結lua狀態的。相信我,我還會回來的。

6 執行緒的狀態和名字

import threading 執行緒名字 class mythread threading.thread def run self if name main t mythread name downloadthread t.start 2.執行緒的執行順序 import time class m...

執行緒的宣告週期和狀態

新建 new 新建立乙個執行緒 就緒 runnable 執行緒物件建立後,其他執行緒呼叫了該物件的start方法,該狀態的執行緒位於可執行執行緒池中,變得可執行,等待獲取cpu的使用權 執行 running 就緒狀態的執行緒或者cpu 的使用權,執行程式 阻塞 blocked 阻塞狀態是執行緒因為某...

Lua 協程和執行緒區別

協程就是協程,不是執行緒。cpu執行單位是執行緒,不是什麼協程。協程,是同步執行,不是並行,只是切了乙個上下文了,為你儲存原來的上下文而已。切到第二個協程時,原來的協程處於掛起狀態。這個特指lua的協程。unity的協程也是這麼回事。lua協程例子 local function run data p...