<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ā)訪問 slice 如何做到優(yōu)雅和安全?

          共 358字,需瀏覽 1分鐘

           ·

          2020-08-18 00:16

          拋出問題

          由于 slice/map 是引用類型,golang 函數(shù)是傳值調(diào)用,所用參數(shù)副本依然是原來的 slice/map, 并發(fā)訪問同一個(gè)資源會(huì)導(dǎo)致競態(tài)條件。

          看下面這段代碼:

          1. package main


          2. import (

          3. "fmt"

          4. "sync"

          5. )


          6. func main() {

          7. var (

          8. slc = []int{}

          9. n = 10000

          10. wg sync.WaitGroup

          11. )


          12. wg.Add(n)

          13. for i := 0; i < n; i++ {

          14. go func() {

          15. slc = append(slc, i)

          16. wg.Done()

          17. }()

          18. }

          19. wg.Wait()


          20. fmt.Println("len:", len(slc))

          21. fmt.Println("done")

          22. }


          23. // Output:

          24. len: 8586

          25. done

          真實(shí)的輸出并沒有達(dá)到我們的預(yù)期,len(slice) < n。問題出在哪?我們都知道slice是對數(shù)組一個(gè)連續(xù)片段的引用,當(dāng) slice 長度增加的時(shí)候,可能底層的數(shù)組會(huì)被換掉。當(dāng)在換底層數(shù)組之前,切片同時(shí)被多個(gè) goroutine 拿到,并執(zhí)行 append 操作。那么很多 goroutine 的 append 結(jié)果會(huì)被覆蓋,導(dǎo)致 n 個(gè) gouroutine append 后,長度小于n。

          那么如何解決這個(gè)問題呢?

          map 在 go 1.9 以后官方就給出了 sync.map 的解決方案,但是如果要并發(fā)訪問 slice 就要自己好好設(shè)計(jì)一下了。下面提供兩種方式,幫助你解決這個(gè)問題。

          方案 1: 加鎖 ?

          1. func main() {

          2. slc := make([]int, 0, 1000)

          3. var wg sync.WaitGroup

          4. var lock sync.Mutex


          5. for i := 0; i < 1000; i++ {

          6. wg.Add(1)

          7. go func(a int) {

          8. defer wg.Done()

          9. // 加?

          10. lock.Lock()

          11. defer lock.Unlock()

          12. slc = append(slc, a)

          13. }(i)

          14. wg.Wait()


          15. }


          16. fmt.Println(len(slc))

          17. }

          優(yōu)點(diǎn)是比較簡單,適合對性能要求不高的場景。

          方案 2:使用 channel 串行化操作

          1. type ServiceData struct {

          2. ch chan int // 用來 同步的channel

          3. data []int // 存儲(chǔ)數(shù)據(jù)的slice

          4. }


          5. func (s *ServiceData) Schedule() {

          6. // 從 channel 接收數(shù)據(jù)

          7. for i := range s.ch {

          8. s.data = append(s.data, i)

          9. }

          10. }


          11. func (s *ServiceData) Close() {

          12. // 最后關(guān)閉 channel

          13. close(s.ch)

          14. }


          15. func (s *ServiceData) AddData(v int) {

          16. s.ch <- v // 發(fā)送數(shù)據(jù)到 channel

          17. }


          18. func NewScheduleJob(size int, done func()) *ServiceData {

          19. s := &ServiceData{

          20. ch: make(chan int, size),

          21. data: make([]int, 0),

          22. }


          23. go func() {

          24. // 并發(fā)地 append 數(shù)據(jù)到 slice

          25. s.Schedule()

          26. done()

          27. }()


          28. return s

          29. }


          30. func main() {

          31. var (

          32. wg sync.WaitGroup

          33. n = 1000

          34. )

          35. c := make(chan struct{})


          36. // new 了這個(gè) job 后,該 job 就開始準(zhǔn)備從 channel 接收數(shù)據(jù)了

          37. s := NewScheduleJob(n, func() { c <- struct{}{} })


          38. wg.Add(n)

          39. for i := 0; i < n; i++ {

          40. go func(v int) {

          41. defer wg.Done()

          42. s.AddData(v)


          43. }(i)

          44. }


          45. wg.Wait()

          46. s.Close()

          47. <-c


          48. fmt.Println(len(s.data))

          49. }

          實(shí)現(xiàn)相對復(fù)雜,優(yōu)點(diǎn)是性能很好,利用了channel的優(yōu)勢




          推薦閱讀



          學(xué)習(xí)交流 Go 語言,掃碼回復(fù)「進(jìn)群」即可


          站長 polarisxu

          自己的原創(chuàng)文章

          不限于 Go 技術(shù)

          職場和創(chuàng)業(yè)經(jīng)驗(yàn)


          Go語言中文網(wǎng)

          每天為你

          分享 Go 知識

          Go愛好者值得關(guān)注


          瀏覽 34
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  免费操比| 精品日韩人妻 | 日韩18页 | 国产无套内精一级毛片三 | 青娱乐无码在线 |