国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

淺談Lua協(xié)程和函數的尾調用

這篇具有很好參考價值的文章主要介紹了淺談Lua協(xié)程和函數的尾調用。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

雖然不經常用到協(xié)程,但是也不能談虎色變。同時,在有些場景,協(xié)程會起到一種不可比擬的作用。所以,了解它,對于一些功能,也會有獨特的思路和想法。

協(xié)程

概念

關于進程和線程的概念就不多說。

那么從多線程的角度來看,協(xié)程和線程有點類似:擁有自己的棧,局部變量和指令指針,又和其他協(xié)程共享全局變量等一切資源。

主要的區(qū)別在:一個多線程程序可以并行運行多個線程,而協(xié)程卻要彼此協(xié)作運行。

什么是協(xié)作運行?

也就是任意指定的時刻,只能有一個協(xié)程運行。

很懵對不對?

層級調用和中斷調用

所有的語言中,都存在層級調用,比如A調用了B,B在執(zhí)行過程中又去調用C,C執(zhí)行之后,返回到B,B執(zhí)行完畢之后,返回到A,最后A執(zhí)行完畢。

這個過程就像棧一樣,先進后出,依次從棧頂執(zhí)行。所以,它也叫調用棧。

層級調用的方式是通過棧來實現的。

中斷調用,就是我在A函數中可以中斷去調用B函數,函數B中可以中斷去調用A。

比如

function A()
	print(1)
	print(2)
	print(3)
end

function B()
	print("a")
	print("b")
	print("c")
end

那么如果是中斷調用,就可能輸出: 1 a b 2 3

協(xié)程的優(yōu)勢

其實一句話總結。

拿著多線程百分之一的價錢,干著多線程的事情,還效率賊高。

這樣的員工誰不喜歡?

這也是Go語言高并發(fā)的原因之一。

怎么理解協(xié)程?

左手畫圓,右手畫方,兩個手同時操作,這個叫并行,線程就是干這個事情的。
左手畫一筆,切換到右手畫一筆,來回交替,最后完成,這叫并發(fā),協(xié)程就是為了并發(fā)而生。

淺談Lua協(xié)程和函數的尾調用,cocos2dx學習之路,游戲開發(fā)的一些總結,lua,cocos2d

那么線程不能完成協(xié)程的事情?

舉個例子,一個作業(yè),可以交給兩個人完成,這叫并行。創(chuàng)建兩個線程就可以了。
那么其中一個人在做事情的時候,突然有人要插入一個比較緊急的事情,這個人就必須停下手中的事情,去處理那個緊急的事情。停下叫阻塞。線程本身是支持阻塞的。

但是這里就有一個問題,不管是創(chuàng)建線程還是切換線程,所帶來的成本遠遠大于用阻塞的方式實現并發(fā),要考慮兩個線程之間的數據同步,加鎖解鎖,臨界問題等等。而協(xié)程并不需要來回切換。所以,并發(fā)的線程越多,使用協(xié)程來代替的性能優(yōu)勢就越明顯。

所以,可以知道協(xié)程不是線程,它的資源是共享的,不需要如同多線程一樣加鎖來避免讀寫沖突。

比如創(chuàng)建一個線程棧需要1M,那么協(xié)程棧只需要幾K,或者幾十K。

協(xié)程的缺點

協(xié)程本質上是單線程,所以它吃不到多核CPU的福利,需要與進程配合才能辦到。

然后協(xié)程也不是那么好控制,需要寫一些代碼進行手動控制它的中斷調用。

Lua的協(xié)程

Lua的協(xié)程是非對稱協(xié)程。

簡單來說,非對稱就是需要兩個函數來控制協(xié)程的執(zhí)行。

Go的協(xié)程是對稱協(xié)程,有興趣可以去了解下。

再說簡單一點就是,非對稱協(xié)程,需要yield函數來掛起,resum函數來恢復,同時,哪里掛起,就恢復到哪里去。

對稱協(xié)程,只需要yield一個操作。

簡單的例子

理解完這個,就很容易理解Lua的協(xié)程代碼。即在哪里yield,下次調用resum,就恢復到y(tǒng)ield那里,繼續(xù)執(zhí)行。

local co = coroutine.create(function()
    print(1)
    coroutine.yield()
    print(2)
    coroutine.yield()
    print(3)
end)

coroutine.resume(co)
coroutine.resume(co)
coroutine.resume(co)

第一次resume,就是執(zhí)行協(xié)程函數co ,print(1)
第二次resume,就是print(2)
第三次resume,就是print(3)

四種狀態(tài)

一個協(xié)程有四種狀態(tài):

  1. 掛起(suspended)
  2. 運行(running)
  3. 正常(normal)
  4. 死亡(dead)

可以通過函數**coroutine.status(co)**來進行檢查協(xié)程的狀態(tài)。

當一個協(xié)程創(chuàng)建的時候,它處于一個掛起狀態(tài),也就是說,協(xié)程被創(chuàng)建,不會自動運行,需要調用函數**coroutine.resume(co)**用來啟動或者再啟動一個協(xié)程的執(zhí)行,將掛起狀態(tài)改成運行狀態(tài)。

當協(xié)程中執(zhí)行到最后的時候,整個協(xié)程就停止了,變成了死亡狀態(tài)。

協(xié)程報錯

由于協(xié)程resume在保護模式下,所有錯誤都會返回給它,即哪怕協(xié)程處于死亡狀態(tài),調用coroutine.resume(co),也不會出現任何錯誤。

同時協(xié)程中的錯誤不容易被發(fā)現,所以需要使用xpcall來進行拋出。

lua5.1需要封裝一層來達到這個目的

--協(xié)程,Lua5.1無法掛C函數,這樣進行處理,協(xié)程中出問題,會拋出錯誤
local coroutCanTrowError = function()
    xpcall(showGetSpecialCollect,__G__TRACKBACK__)
end
self.m_showGetSpecialRune = coroutine.create(coroutCanTrowError)
coroutine.resume(self.m_showGetSpecialRune)

xpcall函數,異常處理函數,是lua強大的異常處理函數,這個以后做分享。

交換數據

lua的協(xié)程主要是通過resume-yield來進行數據交換的。

即第一個resume函數會把所有的額外參數傳遞給協(xié)程的主函數。

什么意思呢?

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

coroutine.resume(co,1,2,3) 

resum函數會把參數都傳遞給協(xié)程的主函數。所以這里輸出co 1 2 3

local co = coroutine.create(function(a,b,c)
	    print("co",a,b,c)
		coroutine.yield(a + b,a - b , c + 2)
	end)

print(coroutine.resume(co,1,2,3))

輸出就是

co 1 2 3

true 3 -1 5

在yield的時候,會把參數都返回給resume,這里有點拗口。
可以這么理解,yield中的參數就是resum的返回值。當然這里要注意的是,resume的返回值第一個是resume是否成功的標志。

這種類似于

local co = coroutine.create(function(a,b,c)
		return a - b
	end)

print(coroutine.resume(co,1,2,3))

輸出 true,-1

也就是協(xié)程主函數的返回值都會變成resume的返回值。

是不是覺得有無限可能了?

但是,值得注意的是,雖然這種機制會帶來很大的靈活性,但是,使用不好,可能會導致代碼的可讀性降低。

著名的生產者和消費者

這是協(xié)程最經典的例子。

即一個生產函數(從文件讀數據),一個消費函數(將讀出來的值寫入另一個文件)。

function producer()
	while true do 
		local x = 1
		send(x)
	end
end

function consumer()
	while true do 
		local x = receive()
		print(x)
    end
end

local list = {}
function send(x)
	table.insert(list,x)
end

function receive()
	if #list > 0 then 
	   local a = table.remove(list)
		return a
    end
end

producer()
consumer()

會發(fā)生什么?

當然,也可以將兩個放到不同的線程中去處理,但是這樣對于數據量大的時候來說就是個災難。

協(xié)程怎么去實現?感興趣的可以去了解下。

function eceive (prod) 
	local status, value = coroutine esume(prod)
	return value 
end 

function send (x) 
	cooutine.yield(x)
end 
function poducer()
	return coroutine.c eate(function () 
		while true do 
			local x = io.read () 
			send (x) 
		end 
	end) 
end 
function filter(prod)
	return coroutine.ceate(func () 
		for line= 1, math.huge do 
		local x = receive (prod) 
		x = string.format(%s ”, line, x)
		send(x)
		end 
		end ) 
end 

function conrumer(prod)
	while true do 
		local x = receivee(prod) 
		io. write (x ,”\n”) 
	end 
end 
conrumer(filter(poducer()))

不用擔心性能問題,因為是協(xié)程,所以任務開銷很小,基本上消費者和生產者是攜手同行。

應用場景

首先,再重復一遍,協(xié)程是并發(fā),不是并行。

有個需求,和我們相關的。

棋盤有4種bonus,停輪之后,每個bouns的效果不一樣,比如翻轉,比如收集等等,效果執(zhí)行完畢之后,再進行下一個,直到結束。

這個是很簡單的需求,可以用for循環(huán)執(zhí)行。

如果加上,bonus在執(zhí)行效果中,會有部分等待,或者延遲效果?那么for循環(huán)就不能滿足,因為在延遲的時候,函數就返回,執(zhí)行下一個for循環(huán)了。

再比如加上,bonus在執(zhí)行效果中,會牽涉到另外一堆邏輯。

等等。

然后,有人會說,遞歸也可以實現。

但是,首先得明白一點,遞歸是個思想,而協(xié)程是個機制。兩個本質上不是一個東西,更何況,遞歸會涉及到其他東西。這個等下會說。

遞歸

遞歸的含義是:在調用一個函數的過程中,直接或者間接調用了函數本身。

一個很簡單的遞歸就是:

local a = nil
a = function(n)
    if n == 1 then 
        return 1
    end
    print("a "..n)
    n = n * a(n - 1)
    print("b "..n)
    return n
end
print(a(5))

請問輸出什么。

輸出

a 5
a 4
a 3
a 2
b 2
b 6
b 24
b 120
120

淺談Lua協(xié)程和函數的尾調用,cocos2dx學習之路,游戲開發(fā)的一些總結,lua,cocos2d

再來溫習下遞歸的特點:

  1. 原來的基礎上不斷“向下/向內”開辟新的內存空間。(即每一次的調用都會增加一層棧,每當函數返回的時候,就減少一層棧。)所以,對于遞歸來說,遞歸的層次越多,很容易導致棧溢出。這也決定了遞歸本身的效率不高。
  2. 遞歸是暫停阻塞,什么是暫停阻塞呢?也就是遞歸調用函數的以下部分是不會被執(zhí)行的,只有返回之后才能執(zhí)行。

說到這里,不得不說lua這個語言有一個非常不錯的優(yōu)化——尾調用。

尾調用

什么是尾調用呢?

一個函數返回您一個函數的返回值。

是不是有點拗口。

我們看下代碼。

function A(x)
	return B(x)
end

通俗的來說,就是當一個函數調用是另一個函數的最后一個動作的時候,該調用才能算上尾調用。

上面例子中,A調用完B之后,就沒有任何邏輯了。這個時候,Lua程序不需要返回函數A所有在得函數,那么程序自然而然就不需要保存任何有關于函數A的棧(stack)信息。

該特性叫“尾調用消除”。

別小看這個特性。

通常,函數在調用的時候,會在內存中形成一個“調用記錄”,它保存了調用位置和內部變量等信息。

例如

function A(x)
  local a = 1
	 local b = B(x)
	 local c = 2
  return a + b + c
end

在程序運行到調用函數B的時候,會在A的調用記錄上方,形成B的調用記錄,等到B返回之后,B的調用記錄才會消失。那么調用的函數越多,就如同棧一樣,依次放到A、B等等的上面,所有的調用記錄就形成了調用棧。

那么想象一下,函數A調用B,B調用C,C調用D…會發(fā)生什么。

棧溢出

棧和堆不一樣,棧是系統(tǒng)分配的,也可以說棧是系統(tǒng)預設的空間,堆是自己申請的。所以,當我們的函數調用層次太深,導致保存調用記錄的空間大于了系統(tǒng)分配的棧,那么就會導致棧溢出。然后各種莫名其妙的Bug就出現了,比如遞歸不返回了;比如調用函數不返回了等等。

這個時候,Lua的尾調用消除就起到了關鍵性作用。它是函數最后一步操作,所以不需要保存外層函數的調用記錄,它里面的所有內部變量等信息都不會再用到了,所以就不需要??臻g去保存這些信息。

那么,可能會有人說,那么我在函數尾部調用另一個函數不就可以了么?

并不是。

function A(x)
	return 1 + B(x)
end

function C(x)
	return true and D(x)
end

上面的兩個函數就不是尾調用。

函數A中,最后一步不是B函數,而是+運算符

函數C中,最后一步不是D,而是and 運算符

function A(x)
	if x > 0 then 
		return B(x)
	end
	return C(x)
end

這樣的函數B和函數C才是尾調用。

可能這樣覺得沒啥,我們做個實驗。

function A(x)
    return 1 + B(x)
end 

function B(x)
    return x * 2
end

print(collectgarbage("count"))
for i = 1,1000000 do 
--print(A(i))
	A(i)
end
print(collectgarbage("count"))

輸出
淺談Lua協(xié)程和函數的尾調用,cocos2dx學習之路,游戲開發(fā)的一些總結,lua,cocos2d
相差:0.09960

那么看下尾調用

function A(x)
	return B(x,1)
end

function B(x,y)
	return x * 2 + y
end

print(collectgarbage("count"))
for i = 1,1000000 do 
--print(A(i))
	A(i)
end
print(collectgarbage("count"))

淺談Lua協(xié)程和函數的尾調用,cocos2dx學習之路,游戲開發(fā)的一些總結,lua,cocos2d
相差是0.035

接近3倍。

可能有人會說,這點性能應該沒啥吧。我想說的是,這里只是一個簡單的計算,本來保存的數據都不大,如果是實際開發(fā)中,需要保存的東西更大了。

尾遞歸

理解了尾調用,那么我們來看看尾遞歸

前面也說了,遞歸依賴棧,非常消耗內存。還有,別以為有些功能就遞歸幾次,就沒有什么性能消耗。那誰又能保證在遞歸的函數中有大量的其他函數調用或者數據處理呢?

畢竟有時候很容易寫出,遞歸函數中調用其他函數,其他函數又調用一堆其他函數這種套娃的代碼。

例如,最開始的代碼

local a = nil
a = function(n)
    if n == 1 then 
        return 1
    end
    return n * a(n - 1)
end
a(5)

這就是一個“不合格”的遞歸函數。

那么尾遞歸怎么寫呢?

local a = nil
a = function(n,m)
    if n == 1 then 
        return m
    end
    return a(n - 1,n * m)
end

a(5,1)

回調

通常會拿協(xié)程同回調也就是callback比較。因為兩者都可以實現異步通信。

比如:

bob.walkto(jane)
bob.say("hello")
jane.say("hello")

當然,不可能這樣運行,那么會導致一起出現。所以有下面的方式。

bob.walto(function (  )
	bob.say(function (  )
		jane.say("hello")
	end,"hello")
end, jane)

如果再多一些呢?

再結合上面說的調用記錄的說法,可能層次深了,發(fā)現咋不回調了。

如果用協(xié)程來實現就是:文章來源地址http://www.zghlxwxcb.cn/news/detail-692957.html

function runAsyncFunc( func, ... )
	local current = coroutine.running
	func(function (  )
		coroutine.resume(current)
	end, ...)
	coroutine.yield()
end
 
coroutine.create(function (  )
	runAsyncFunc(bob.walkto, jane)
	runAsyncFunc(bob.say, "hello")
	jane.say("hello")
end)
 
coroutine.resume(co)

到了這里,關于淺談Lua協(xié)程和函數的尾調用的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【Unity每日一記】“調皮的協(xié)程”,協(xié)程和多線程的區(qū)別在哪里

    【Unity每日一記】“調皮的協(xié)程”,協(xié)程和多線程的區(qū)別在哪里

    ?????個人主頁 :@元宇宙-秩沅 ????? hallo 歡迎 點贊?? 收藏? 留言?? 加關注?! ????? 本文由 秩沅 原創(chuàng) ????? 收錄于專欄 : unity每日一記 ?【軟件設計師高頻考點暴擊】 ?【Unityc#專題篇】之c#系統(tǒng)化大禮包】 ?【unity數據持久化】數據管理類_PlayerPrfs ?【u

    2024年02月05日
    瀏覽(56)
  • cocos2dx學習之UIListView.h

    /**************************************************************************** Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \\\"Software\\\"), to deal in the Software without restriction, including witho

    2024年02月11日
    瀏覽(18)
  • cocos2dx學習之UIListView.cpp

    /**************************************************************************** Copyright (c) 2013-2017 Chukong Technologies Inc. http://www.cocos2d-x.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \\\"Software\\\"), to deal in the Software without restriction, including witho

    2024年02月11日
    瀏覽(27)
  • cocos2dx上做邊下邊玩小包熱更

    因為公司業(yè)務需求需要做邊下邊玩的小包下載,在這里記錄一下思路 下載庫我使用的是cocos2dx 4.x的CCDownloader來下載文件 大體思路就是hook住fileutils中的getFileData函數和isFileExist函數。 isFileExist:無論初始包里文件是否存在,只要文件是游戲的資源文件這里都要返回“文件存在”

    2024年02月03日
    瀏覽(20)
  • cocos2dx ??Animate3D(二)

    扭曲旋轉特效 源碼 示例 瓷磚晃動特效 源碼 示例 破碎的3D瓷磚特效 源碼 示例 瓷磚洗牌特效 源碼 示例 FadeOutTRTiles :淡出效果,從左下角到右上角 FadeOutBLTiles :淡出效果,從右上角到左下角 FadeOutUpTiles :折疊效果,從下到上 FadeOutDownTiles :折疊效果,從上到下 示例 方塊消

    2024年02月05日
    瀏覽(29)
  • unity協(xié)程 Start/StopCoroutine() 結束協(xié)程和再次啟動協(xié)程存在的問題和解決方案

    unity協(xié)程 Start/StopCoroutine() 結束協(xié)程和再次啟動協(xié)程存在的問題和解決方案

    僅用于記錄遇到的問題和解決方案。 快速閱覽: 一、結束協(xié)程無效: 協(xié)程方法需要單獨存一份private IEnumerator myTest,再開始和結束不直接傳入方法名,而是使用這份保存的myTest進行開始和結束。 二、再次開啟協(xié)程時少跑了幾行代碼: 再次開始同一個方法名的協(xié)程時,不是從

    2024年02月15日
    瀏覽(25)
  • cocos2dx游戲項目,集成到其他安卓項目工程之中!

    cocos2dx游戲項目,集成到其他安卓項目工程之中!

    公司,想優(yōu)化掉,在app中,以webview方式,加載游戲的方式。以安卓項目為例,改成:游戲項目導出安卓工程,可直接使用的aar資源。 安裝配置原生開發(fā)環(huán)境,可以參考官方文檔:https://docs.cocos.com/creator/2.4/manual/zh/publish/setup-native-development.html 備注:本人使用cocoscreator版本為:

    2024年02月05日
    瀏覽(29)
  • 使用C語言構建一個獨立棧協(xié)程和共享棧協(xié)程的任務調度系統(tǒng)

    使用了標準庫頭文件 setjmp.h 中的 setjmp 和 longjmp 兩個函數,構建了一個簡單的查詢式協(xié)作多任務系統(tǒng),支持 獨立棧 和 共享棧 兩種任務。 其中涉及到獲取和設置棧的地址操作,因此還需要根據不同平臺提供獲取和設置棧的地址操作(一般是匯編語言,因為涉及到寄存器) 該

    2024年02月19日
    瀏覽(25)
  • 使用C++20協(xié)程和io_uring優(yōu)雅地實現異步IO

    距離2020年已經過去很久了,各大編譯器對于C++20各項標準的支持也日趨完善,無棧協(xié)程也是其中之一,所以我就嘗試著拿協(xié)程與 io_uring 實現了一下proactor模式,這篇文章用來記錄一下我的設計和想法。除此之外,我們能在網絡上找到許多優(yōu)秀的C++20協(xié)程的教程以及許多優(yōu)秀的

    2024年03月27日
    瀏覽(21)
  • 《入門級-Cocos2dx4.0 塔防游戲開發(fā)》---第六課:歡迎界面開發(fā)(四、自定義精靈)

    《入門級-Cocos2dx4.0 塔防游戲開發(fā)》---第六課:歡迎界面開發(fā)(四、自定義精靈)

    目錄 ?一、開發(fā)環(huán)境 二、開發(fā)內容 2. 1 新建自定義精靈 2.2 在welcome中創(chuàng)建新的menu 2.3 消息綁定以及消息響應 三、演示效果 四、知識點 4.1 內部消息響應 4.2 字體精靈說明 4.3?CC_SYNTHESIZE宏 操作系統(tǒng):UOS1060專業(yè)版本。 cocos2dx:版本4.0 環(huán)境搭建教程:統(tǒng)信UOS下配置安裝cocos2dx開發(fā)

    2024年02月11日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包