<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          發(fā)現(xiàn)conc并發(fā)庫(kù)一個(gè)有趣的問(wèn)題

          共 3798字,需瀏覽 8分鐘

           ·

          2023-01-16 19:51

          上周看到一個(gè)新庫(kù)conc,

          better structured concurrency for go.

          這個(gè)庫(kù)的目標(biāo)是:

          • 更難出現(xiàn)goroutine泄漏

          • 優(yōu)雅處理panic

          • 使并發(fā)的代碼更易讀


          我們一條條細(xì)說(shuō)


          Make it harder to leak goroutines

          goroutine泄漏還是很常見的。

          日常我們使用go的時(shí)候直接go func開啟一個(gè)goroutine,寫上對(duì)應(yīng)的邏輯,g會(huì)進(jìn)入到某個(gè)p的本地隊(duì)列,最終由p綁定的m執(zhí)行這個(gè)goroutine。

          你能保證一個(gè)goroutine在某個(gè)時(shí)刻一定會(huì)結(jié)束它的生命周期嗎?

          搜了下著名開源項(xiàng)目etcd,goroutine leak還真不少

          ffaa212593f91a1d88bc8edbea3e3725.webp

          看其中一個(gè)簡(jiǎn)單的泄漏bug

          203dc654890b3d0e1a6d41b0811e4981.webp

          done是一個(gè)無(wú)緩沖的chan,一開始接收動(dòng)作在最下面,因?yàn)橹虚g還有一些沒(méi)展開的代碼可能會(huì)導(dǎo)致程序不會(huì)執(zhí)行到<-done,然后goroutine就會(huì)發(fā)生泄漏。

          解決方法就是通過(guò)defer保證一定會(huì)執(zhí)行<-done

          那么conc是如何做的?

          ef0a49fdee36fd61d9ce995a2dc3d042.webp

          conc的理念是程序中的每一個(gè)goroutine由一個(gè)owner創(chuàng)建,歸屬于owner。一個(gè)owner確保它擁有的所有g(shù)oroutine正常退出,這里的owner也就是conc.WaitGroup。ps:這個(gè)結(jié)構(gòu)是不是超級(jí)熟悉。

          但是這真的能像他說(shuō)的那樣Make it harder to leak goroutines嗎?

          goroutine的泄漏問(wèn)題取決于用戶對(duì)goroutine能正確退出的邏輯保證,和你如何封裝沒(méi)關(guān)系吧?

          在我看來(lái)conc和標(biāo)準(zhǔn)庫(kù)的sync.WaitGroup一樣,只是等待所有g(shù)oroutine執(zhí)行完畢,用于檢測(cè)到這個(gè)行為。

          要是goroutine里面含有泄漏的bug,該泄漏還得泄漏,Wait該等待還得老實(shí)等待。


          Handle panics gracefully

          如果直接使用go func,那么可能每一個(gè)goroutine都得寫上recover,所以一般我們?cè)谑褂胓oroutine的時(shí)候,都是自己封裝一個(gè)GoSafe函數(shù)。

          這樣就可以在里面統(tǒng)一捕獲panic,然后打包調(diào)用棧一些信息,進(jìn)一步處理。

          在conc中,每個(gè)goroutine有owner概念,所以是由owner捕獲goroutine的panic。

          97f72d04d3cfcac39e30eb996d6ed88a.webp

          只會(huì)記錄第一個(gè)panic的goroutine堆棧信息。然后操作Wait的時(shí)候

          847a1d013f5c05d4d3bac71b52334641.webp

          超級(jí)粗暴。當(dāng)conc.WaitGroup里面任何一個(gè)goroutine發(fā)生panic,調(diào)用wg.Wait()的時(shí)候就會(huì)panic,把goroutine panic的堆棧信息作為panic的值。

          寫這篇文章的時(shí)候看到他們?cè)趇ssue上討論添加一個(gè)類似WaitSafe()函數(shù)[1]


          Make concurrent code easier to read

          這個(gè)還是節(jié)省了一些工作的,作者給了幾個(gè)例子。

          上面我們看到的WaitGroup,不再需要用戶執(zhí)行Add和Done操作了。同時(shí)內(nèi)部捕獲panic,雖然處理有點(diǎn)粗暴

          fc169a350419c5541287b5e3e545c2b0.webp

          除此之外,控制goroutine數(shù)量來(lái)并發(fā)處理批量數(shù)據(jù)的例子。

          09ff05e03d15768b5beb4d439f78a0ea.webp

          1213d0c96bde6d41d1efc0c995b5cb54.webp

          可以看出,確實(shí)省了很多的操作,其他例子可以自行查看。

          上面我們說(shuō)到pool,其實(shí)就是一個(gè)并發(fā)執(zhí)行任務(wù)的worker池,之前文章也介紹過(guò)這種模式。

          conc里面有幾個(gè)類型的pool:ContextPool,ErrorPool,ResultPool,ResultContextPool,ResultErrorPool。

          它們都基于最基礎(chǔ)的Pool結(jié)構(gòu)

          ed50e16d96372cc7c1397f89bb9d3141.webp

          limiter就是一個(gè)很簡(jiǎn)單的用chan控制worker goroutine數(shù)量

          319fd3a6d638a4b4f820b71db9560143.webp

          核心邏輯也很簡(jiǎn)單,

          f6d2c89209dfcc1cc0a6265c26b518e5.webp

          如果沒(méi)有設(shè)置limiter,那么優(yōu)先找空閑的worker。否則就創(chuàng)建一個(gè)新worker,然后投遞任務(wù)進(jìn)去。

          設(shè)置了limiter, 達(dá)到了limter worker數(shù)量上限,那就只能把任務(wù)投遞給空閑的worker,沒(méi)有空閑就阻塞等著。

          如果沒(méi)有達(dá)到上限,空閑worker也存在,那就由select隨機(jī)選擇。 否則的話就創(chuàng)建一個(gè)新的worker。


          看代碼的時(shí)候發(fā)現(xiàn)這里面有個(gè)問(wèn)題

          dfe22fa3be963fc6939cf673f1e45293.webp

          這個(gè)函數(shù)會(huì)返回一個(gè)新的Pool,上面我說(shuō)過(guò)limiter是一個(gè)chan結(jié)構(gòu),在go中chan是一個(gè)引用類型,所以這里對(duì)limiter就是一個(gè)淺拷貝。

          因此,下面這段代碼

          2e60b476887a79afeceec5dcd9d4ba82.webp

          正因?yàn)檫@種“特性”,如果我們寫了下面的代碼,

          054272d0b89b45ed911e93f1d5fee026.webp

          ep.Go里面的邏輯永遠(yuǎn)都沒(méi)機(jī)會(huì)執(zhí)行。

          原因就在于,第一個(gè)loop創(chuàng)建的時(shí)候限制了goroutine數(shù)量。然后執(zhí)行兩次p.Go,這樣就會(huì)創(chuàng)建兩個(gè)只屬于p的workers,同時(shí)也讓limiter到達(dá)限制數(shù)。

          當(dāng)ep想要執(zhí)行ep.Go的時(shí)候,只能執(zhí)行p.tasks <- f,但是這時(shí)候ep還沒(méi)有機(jī)會(huì)創(chuàng)建屬于自己的worker,所以會(huì)阻塞到死。

          我提了一個(gè)pr[2],其實(shí)就是把上面的淺拷貝換成深拷貝

          6904f9c5aa8e7ec0c6ec9c8007fa7cae.webp

          但是作者回復(fù)說(shuō),

          Currently calling any configuration methods on the pool after calling pool.Go() is unsupported because the configuration methods take ownership of and mutate the pool. This might not be ideal though since ownership can't actually be enforced with Go. It would be less of a foot gun to just return a fully copy each time.

          我理解的意思就是不支持我這么玩,簡(jiǎn)單的說(shuō)不支持在執(zhí)行pool.Go()后調(diào)用這些操作 ??。

          [1] https://github.com/sourcegraph/conc/issues/29

          [2] https://github.com/sourcegraph/conc/issues/43



          推薦閱讀


          福利
          我為大家整理了一份 從入門到進(jìn)階的Go學(xué)習(xí)資料禮包 ,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。 關(guān)注公眾號(hào) 「polarisxu」,回復(fù)? ebook ?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 159
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中国美女一级大片 | 又粗又大操逼视频 | 2014AV天堂网 | 成人黄色在线观看 | 影音先锋全部av鲁色 |