日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在LUA中使用异步IO的思考

發(fā)布時間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在LUA中使用异步IO的思考 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

LUA協(xié)程的介紹

lua 有一套非常高效的協(xié)程機制, 這一套實現(xiàn)非常輕量級, 雖然簡單意味著高效, 然而它并不是真正意義上的對稱式協(xié)程. lua中使用 coroutine.create 來創(chuàng)建一個協(xié)程, 使用 coroutine.resume 來執(zhí)行協(xié)程。使用 coroutine.yield 來讓出當(dāng)前正在執(zhí)行的協(xié)程. 通過這兩個函數(shù),你可以在兩個協(xié)程之間相互傳遞參數(shù), 直到協(xié)程執(zhí)行完成.

local function f(a1)-- 讓出協(xié)程,傳遞 a1+1 值給resume函數(shù)local ret = coroutine.yield(a1+1)-- 讓出協(xié)程,傳遞 ret+1 值給resume函數(shù)return coroutine.yield(ret+1) end-- 這里創(chuàng)建一個協(xié)程 local co = coroutine.create(f) -- 執(zhí)行f 協(xié)程, 當(dāng)前協(xié)程會被掛起 print(coroutine.resume(co, 1)) print(coroutine.resume(co, 12))

通過上面的例子可以看出, lua的協(xié)程只能yield 給 resume方. 也就是說 你在協(xié)程內(nèi)任意地方y(tǒng)ield 那么resume 會立刻返回.

異步IO

假定我們希望使用一套異步io庫來結(jié)合LUA的協(xié)程來做開發(fā), 會遇到哪些問題呢?首先異步IO會有一個事件循環(huán), 比如 libuv 的 uv_run. 它們所有的操作都是通過回調(diào)來完成的. 如果采用完全相同的編程方式,這肯定沒太多意義, 對簡化編程幫助也不大。編程同步化更加符合人的思維習(xí)慣, 開發(fā)起來也會更加簡單.

想的方案是, 當(dāng)我們調(diào)用一個io操作的時候, 掛起當(dāng)前協(xié)程, 在收到網(wǎng)絡(luò)事件的時候再恢復(fù)它的執(zhí)行. 編程方法將會變成下面這樣:

-- 連接通信函數(shù) local function connhandler(conn)while true do--- 循環(huán)從客戶端讀取數(shù)據(jù)local data, reason = conn.read(10000)if 110 == data then-- 超時elseif 'string' == type(data) then--讀取到了數(shù)據(jù)conn.write(data)else-- 出錯了, 或者連接斷開breakendendprint('conn has gone') end-- 服務(wù)器循環(huán)等待客戶端連接 local function serverloop(server)while true do-- 等待一個連接local retv, message = server.wait(10000)if false == conn thenbreakelseif 110 == conn then-- wait timeoutelse-- 得到了一個連接 retv is conncoroutine.resume(connhandler, retv)endend endlocal server = tcp.server('0.0.0.0', 80) coroutine.resume(serverloop)--這里是一個事件循環(huán) uv.run()

這里完美將回調(diào)函數(shù)給隱藏了, 使用一個 while 循環(huán)來等待客戶端的連接. 在得到一個連接的時候, 我們創(chuàng)建了一個協(xié)程來處理這個連接的所有業(yè)務(wù).
在 和連接通信的時候 conn.read conn.write 我們會掛起當(dāng)前協(xié)程, 將控制權(quán)轉(zhuǎn)移給 serverloop 以讓 server 能繼續(xù)去接受新的連接. 這樣做的并發(fā)能力還是不錯的. 通常情況下, 執(zhí)行不會有問題的。例如如下的流程:

  • connhandler 進(jìn)行IO操作 掛起 -> serverloop 開始執(zhí)行.
  • serverloop 執(zhí)行等待連接操作, 掛起, 這個時候進(jìn)入了 lua的 mainthread.
  • 第一個連接有數(shù)據(jù)到達(dá).
  • mainthread 恢復(fù) connhandler 協(xié)程.
  • lua 的一個協(xié)程創(chuàng)建后, 你可以在任何地方 resume 它, 然而它的 yield 卻只能 將執(zhí)行控制權(quán)交給 resume 方.

    然而 這樣做也還是存在一些問題. 繼續(xù)分析一下:
    原因在于 connhandler 掛起并不總是為了等待 conn的io操作返回. 假定我們在 connhandler 中執(zhí)行了另外一個異步io, 比如 發(fā)起一個http請求, 這個時候 這個conn 剛好有數(shù)據(jù)回來. 整套執(zhí)行流程就亂了. _

    解決辦法:

    我們只有在 顯式uv_read_start, 在lua中 conn.read 的時候執(zhí)行 uv_read_start, on_read 回調(diào)中取到數(shù)據(jù)后立即 uv_read_stop. 即可.

    接下來我們可以放心地在 connhandler 去調(diào)用其它的異步IO.操作. 我們可以確保一個io操作導(dǎo)致的等待, 只會被該io操作的回調(diào)結(jié)果 resume 即可.

    如何做?

    下面給出示意代碼:

    // tcp.read 函數(shù)uv_read_start(conn, uv_onread);lua_pushthread(L);conn->read_ref = luaL_ref(L, LUA_REGISTERYINDEX, -1);return lua_yield(L, 0);// 讀取到數(shù)據(jù)的回調(diào)結(jié)果.uv_onread(...)uv_read_stop(conn)// resume read_ref 協(xié)程 并傳遞讀取結(jié)果.

    至此, 我們已經(jīng)實現(xiàn)了將異步io同步化的改造, 在框架中 我們僅需要實現(xiàn) tcpserver tcpclient udp pipe 進(jìn)程池 等的封裝, 包括ssl支持的具體的上層應(yīng)用可以全部使用lua來完成.

    Channel 實現(xiàn)

    本來打算利用純lua來實現(xiàn)channel的, 畢竟 協(xié)程之間的相互 yield 和resume 不就是現(xiàn)成的channel么? 然而,看起來很香, 事實上可能沒那么簡單. 原因是 脫離了 libuv 的事件循環(huán)后,純語言層 進(jìn)行 yield resume 優(yōu)先級太高了. 假定你在業(yè)務(wù)場景中死循環(huán)使用channel 通信, 只在兩個協(xié)程中間相互 yield resume的話, 這個時候 main thread 將永遠(yuǎn)沒機會執(zhí)行。其它io事件都沒機會執(zhí)行.

    雖然 lua 的協(xié)程之間的 yield resume 效率很高, 但還是不得不做一些取舍, 借助 uv_async_send 來實現(xiàn) channel.

  • push 的時候, 我們將數(shù)據(jù)加入到一個隊列中. 并執(zhí)行 uv_async_send.
  • channel->pop 執(zhí)行的時候, 先檢查隊列,如果有數(shù)據(jù)直接返回這條數(shù)據(jù). 如果沒有. 掛起當(dāng)前協(xié)程. 等到 uv_async_cb 的 resume 喚醒.
  • 可以使用 uv_timer 來實現(xiàn)超時. 這里不多作說明.
  • 后注

    這套實現(xiàn)已經(jīng)在項目中得到應(yīng)用, 目前工作良好.

    總結(jié)

    以上是生活随笔為你收集整理的在LUA中使用异步IO的思考的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。