Lua 協程應用

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

即使我死了,埋在土地裡,我也要用我腐爛的聲帶喊出:閑置CPU是可恥。——孔子

進程,線程?並行,並發?

由於單核CPU效能過剩,而如此高的效能卻只能運行一個程式無疑是極大的浪費,因此多任務作業系統應運而生。

作業系統把每個任務映射為一個進程,通過CPU輪詢讓每個進程輪流程執行,從而讓人誤以為所有進程都在同時運行,實際上再同一時刻只有一個進程運行,這叫並發。當CPU有多核或者多線程的時候,在同一時刻就會同時有多個進程運行,這叫並行

隨著時代的進步,越來越多的商務邏輯需要高並發,需要高效能。因為建立進程,切換進程,進程間通訊,成本之高,使得多個進程協同運行並不能滿足需求,於是多線程應運而生。通過在每個進程裡建立多個線程,這些線程共用一個進程資料,這幾乎彌補了多進程的所有缺點。當今主流作業系統都是多線程作業系統,例如:windows,linux,線程為作業系統最小調度單位,通過調度器,為每一個線程分配時間片。

隨著硬體效能的提升,隨著時間的考驗,多進程的缺點變成了多線程的缺點:建立線程,切換線程,線程間通訊,成本之高。因為單核CPU效能已經足夠,很多時候需要的是並發,而不是並行。而進程和線程都是由系統接管,並發還是並行都由系統說了算。因此人們渴望只並發的多線程,從而協程應運而生。

什麼是協程?

設想一下:

多個線程處理各自的隊列,當隊列有任務時,線程依次執行,當隊列為空白時,線程休眠,如此迴圈。

如果線程數量上千甚至上萬,這些線程同一時刻只有少部分隊列有任務,那麼作業系統會頻繁的掛起線程,啟用線程,並且所有線程都會佔用系統資源,這會導致系統不堪重負,即使只有少數線程運行,也會讓系統變得緩慢,甚至每個線程都需要鎖,雖然有許多無鎖演算法,但這會讓編程難度從普通上升到地獄,而操作線程,鎖的時間也許遠遠大於線程執行任務的時間。

如果我們可以擁有自己的線程調度器,可以在應用程式層隨意調度線程,避免線程進入核心狀態,避免線程爭奪資源,降低線程切換開銷,可以在單核CPU高並發榨乾效能,這樣的多線程就是協程

如果用協程來完成上述任務,可以解決上述的所有缺點。
由於調度器太底層,幾乎把整個語言固定了,因此只有那些為了高並發而生的語言,才會有調度器,例如:GoLang,ErLang等,這些語言適合編寫高並發程式。對於那些不是為了高並發而生的語言,它們也需要協程,但是它們不需要調度器,它們只要手動調度,這就已經足夠了。

用協程取代非同步回調

Lua 有協程,沒有調度器,即使這樣,協程依舊是Lua最強特性沒有之一,這一特性經常被生手忽略,因為他們根本想不到該怎樣使用協程。

function f()    f1()    f2()    f3()end

上述代碼邏輯很清晰,f()函數按順序執行了f1()f2()f3()出於某些原因,也許動畫延遲也許網路請求也許有意延遲f1()f2()不能馬上得到結果,同時,也不能讓線程卡在這等著。因此,這裡就變成了非同步需求,代碼可能變成下面這樣:

function f()    f1(function() f2(f3) end)end

以上邏輯看起來很依舊很清晰,f1()f2(),都接受一個函數作為非同步回調,但是在f1()傳參時,明顯出現了不和諧代碼,因為f3()要作為f2()的參數,需要用function() end產生一個閉包傳遞。但是,一旦需要執行更多的函數,代碼將慘不忍睹:

function f()    f1(function() f2(function() f3(function() f4(f5) end) end) end)end

回到前一段代碼:

function f()    f1(function() f2(f3) end)end

這段代碼非同步回呼函數只有3個,看似邏輯清晰,實際上存在重大隱患,從f()函數體來看,只能看出f1()f2()分別接受一個函數作為參數,並不能明確f1()f2()f3() 是否按順序調用(也許內部壓根就沒有碰這個參數), 因而使得f()邏輯很不清晰,甚至連內部執行順序都不能保障。如果存在大量非同步回調,邏輯混亂程度更是不敢想象,俗稱:回調地獄

有沒有可能讓非同步呼叫,也可以像同步調用那樣寫呢?用協程可以做到。

local function async(handler)    local runn = coroutine.running()    handler(function()         coroutine.resume(runn)    end)    coroutine.yield()endfunction f()    local co = coroutine.create(function()        async(f1)        async(f2)        async(f3)        ...        async(fN)    end)    coroutine.resume(co)end

f()內部將f1()f2()...fN()按順序非同步呼叫,這段代碼看起來是同步的,但它的的確確是非同步。協程的確可以優雅解決回調地獄問題,當然,這個寫法並不固定,可根據具體需求進行編碼。

上述代碼原理是:
用協程去執行第一個非同步函數,同時跳回主程式,因為協程跟主程式在同一線程,因此,在協程裡調用跟在主程式調用是一樣的,當非同步呼叫完成再跳回協程,繼續下一個非同步呼叫,如此迴圈。

非同步回調並非沒有優勢。比如:可以把簡單問題搞得複雜,把複雜問題搞出很多問題,讓開發人員每天都過得充實。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.