<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ā)控制模式:orDone的兩種實(shí)現(xiàn)

          共 5002字,需瀏覽 11分鐘

           ·

          2021-09-07 12:34

          文章目錄

          • 方式一 遞歸

          • 方式二 利用反射

          • 性能差異

          orDone 是一種并發(fā)控制模式,旨在多任務(wù)場(chǎng)景下實(shí)現(xiàn),有一個(gè)任務(wù)成功返回即立即結(jié)束等待。

          今天我們來(lái)看下兩種不同的實(shí)現(xiàn)方式:

          方式一 遞歸

          利用二分法遞歸, 將所有待監(jiān)聽(tīng)信號(hào)的chanselect起來(lái),

          當(dāng)有第一個(gè)chan返回時(shí),close orDone 來(lái)通知讀取方已有第一個(gè)任務(wù)返回

          代碼如下比較直觀:

          // 傳入多個(gè)并發(fā)chan,返回是否結(jié)束的 orDone chan
          func Or(channels ...<-chan interface{}) <-chan interface{} {
           // 只有零個(gè)或者1個(gè)chan
           switch len(channels) {
           case 0:
                  // 返回nil, 讓讀取阻塞等待
            return nil
           case 1:
            return channels[0]
           }

           orDone := make(chan interface{})
           go func() {
                  // 返回時(shí)利用close做結(jié)束信號(hào)的廣播
            defer close(orDone)

                  // 利用select監(jiān)聽(tīng)第一個(gè)chan的返回
            switch len(channels) {
            case 2// 直接select
             select {
             case <-channels[0]:
             case <-channels[1]:
             }
            default// 二分法遞歸處理
             m := len(channels) / 2
             select {
             case <-Or(channels[:m]...):
             case <-Or(channels[m:]...):
             }
            }
           }()

           return orDone
          }

          方式二 利用反射

          這里要用到reflect.SelectCase, 他可以描述一種selectcase, 來(lái)指明其接受的是chan的讀取或發(fā)送

          type SelectCase struct {
           Dir  SelectDir // direction of case
           Chan Value     // channel to use (for send or receive)
           Send Value     // value to send (for send)
          }

          有了這個(gè),就可以之間遍歷,不用遞歸來(lái)實(shí)現(xiàn)有限的select case構(gòu)造

          最后用reflect.Select(cases)監(jiān)聽(tīng)信號(hào)就可以了,代碼如下:

          func OrInReflect(channels ...<-chan interface{}) <-chan interface{} {
           // 只有0個(gè)或者1個(gè)
           switch len(channels) {
           case 0:
            return nil
           case 1:
            return channels[0]
           }

           orDone := make(chan interface{})
           go func() {
            defer close(orDone)
            // 利用反射構(gòu)建SelectCase,這里是讀取
            var cases []reflect.SelectCase
            for _, c := range channels {
             cases = append(cases, reflect.SelectCase{
              Dir:  reflect.SelectRecv,
              Chan: reflect.ValueOf(c),
             })
            }

            // 隨機(jī)選擇一個(gè)可用的case
            reflect.Select(cases)
           }()

           return orDone
          }

          性能差異

          這兩種都可以支持大量chan的信號(hào)監(jiān)聽(tīng),那性能差異大么

          雖說(shuō)遞歸開(kāi)銷(xiāo)肯定不小,反射也不一定效率高,拿個(gè)壓測(cè)來(lái)試試吧

          先構(gòu)造一下大量并發(fā)chan

          func repeat(
           done <-chan interface{},
              // 外部傳入done控制是否結(jié)束
           values ...interface{},
          )
           <-chan interface
          {} {
           valueStream := make(chan interface{})
           go func() {
                  // 返回時(shí)釋放
            defer close(valueStream)
            for {
             for _, v := range values {
              select {
              case <-done:
               return
              case valueStream <- v:
              }
             }
            }
           }()
           return valueStream
          }

          然后壓測(cè)

          func BenchmarkOr(b *testing.B) {
           done := make(chan interface{})
           defer close(done)
           num := 100
           streams := make([]<-chan interface{}, num)
           for i := range streams {
            streams[i] = repeat(done, []int{123})
           }
           b.ResetTimer()
           for i := 0; i < b.N; i++ {
            <-Or(streams...)
           }
          }

          func BenchmarkOrInReflect(b *testing.B) {
           // 代碼類似
          }

          跑了下結(jié)果如下:

          goos: darwin
          goarch: amd64
          pkg: github.com/NewbMiao/Dig101-Go/concurrency/channel/schedule/orDone
          BenchmarkOr-12                 31815      38136 ns/op     9551 B/op       99 allocs/op
          BenchmarkOrInReflect-12        55797      21755 ns/op    25232 B/op      112 allocs/op

          可以看出,大量并發(fā)chan場(chǎng)景下, 反射使用內(nèi)存更多些,但速度更快。



          推薦閱讀


          福利

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

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          <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>
                  精品久久久久久久久久久久久久久 | 精品撸| 中国一级毛片视频 | 欧美视频网站中文字幕 | 欧美性爱久久平台 |