Golang在并行的goroutine之间共享通道时可能会出现竞态条件。

huangapple go评论74阅读模式
英文:

Golang race condition when sharing channel across parallel goroutines

问题

我正在编写这个示例代码来教自己如何在并行的goroutine之间共享通道,但是我遇到了竞态条件。程序应该启动与系统上可用的CPU数量相同的goroutine。第一个访问bl通道的goroutine立即将通道设置为false,以便其他goroutine无法访问遍历st通道的循环。其他的goroutine应该在第一个访问bl通道的goroutine从st通道读取并打印每个值时结束。

当我运行代码时,我得到以下错误:

$ go run share.go
me: 1
me: 0
me: 2
me: 3
done: 0
done: 2
time: 2s i: 1 n: 0
time: 1s i: 1 n: 1
time: 0s i: 1 n: 2
time: 2s i: 1 n: 3
time: 2s i: 1 n: 4
time: 2s i: 1 n: 5
time: 0s i: 1 n: 6
time: 2s i: 1 n: 7
time: 2s i: 1 n: 8
time: 2s i: 1 n: 9
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc4200701bc)
/usr/local/go/src/runtime/sema.go:47 +0x30
sync.(*WaitGroup).Wait(0xc4200701b0)
/usr/local/go/src/sync/waitgroup.go:131 +0x97
main.main()
/share.go:80 +0x1ef

goroutine 36 [chan receive]:
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x1)
/share.go:64 +0x23e
created by main.main
/share.go:73 +0x127

goroutine 38 [chan send]:
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x3)
/share.go:61 +0x1ef
created by main.main
/share.go:73 +0x127
exit status 2

我不确定如何处理bl通道的竞态条件。似乎最后一个goroutine在尝试从bl通道读取时被阻塞,但是因为它之前的goroutine还没有插入false,所以没有可读取的内容。不确定我的直觉是否正确。我在代码周围放置了打印语句,至少看起来是这样发生的。我还尝试在bl通道周围放置了一个if语句,但是出现了相同的问题。我还将bl <- false移动到if can {块内部,但也没有起作用。我该如何解决这个问题?

英文:

I'm writing this example code to teach myself how to share channels across parallel goroutines and I'm hitting a race condition. The program should start as many goroutines as there are available CPUs on the system. The first goroutine to access the bl channel immediately sets the channel to contain false so that no other goroutines can access the loop that ranges over the st channel. The other goroutines should end as the first goroutine that accessed the bl channel reads from the st channel and prints each value.

package main

import (
	&quot;fmt&quot;
	&quot;runtime&quot;
	&quot;strconv&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
	&quot;sync&quot;
)

func main () {
	runtime.GOMAXPROCS(runtime.NumCPU())
	var wg sync.WaitGroup
	st := make(chan string)
    bl := make(chan bool)
    go func() {bl &lt;- true}()

    for i := 0; i &lt; runtime.NumCPU(); i++ {
    	wg.Add(1)
        go func(x int) {
        	defer wg.Done()
            fmt.Println(&quot;me: &quot;, strconv.Itoa(x))

            can := &lt;- bl
            bl &lt;- false
    
            if can {
                for val := range st {
                	t      := strconv.Itoa(rand.Int()%3)+&quot;s&quot;
                	dur, _ := time.ParseDuration(t)
                	time.Sleep(dur)

                        fmt.Println(&quot;time: &quot;, t,&quot; i: &quot;, strconv.Itoa(x), val)
    		    }
            }
            fmt.Println(&quot;done: &quot;, strconv.Itoa(x))
        }(i)
    }

    for i := 0; i &lt; 10; i++ {
    	st &lt;- &quot;n: &quot;+strconv.Itoa(i)
    }

    wg.Wait()
    close(st)
}

When I run the code, I get the following error:

$ go run share.go 
me:  1
me:  0
me:  2
me:  3
done:  0
done:  2
time:  2s  i:  1 n: 0
time:  1s  i:  1 n: 1
time:  0s  i:  1 n: 2
time:  2s  i:  1 n: 3
time:  2s  i:  1 n: 4
time:  2s  i:  1 n: 5
time:  0s  i:  1 n: 6
time:  2s  i:  1 n: 7
time:  2s  i:  1 n: 8
time:  2s  i:  1 n: 9
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc4200701bc)
	/usr/local/go/src/runtime/sema.go:47 +0x30
sync.(*WaitGroup).Wait(0xc4200701b0)
	/usr/local/go/src/sync/waitgroup.go:131 +0x97
main.main()
	/share.go:80 +0x1ef

goroutine 36 [chan receive]:
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x1)
	/share.go:64 +0x23e
created by main.main
	/share.go:73 +0x127

goroutine 38 [chan send]:
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x3)
	/share.go:61 +0x1ef
created by main.main
	/share.go:73 +0x127
exit status 2

I'm not sure how to handle this race condition for the bl channel. It seems that the last goroutine is stuck trying to read from the bl channel but there's nothing for it to read because the goroutine before it hasn't inserted false yet. Not sure if my intuition is correct. I placed print lines around the code and that's what seems to be happening at least. I also tried putting an if around the bl channel, but that ended up with the same issue. I also moved bl &lt;- false inside the if can { block, but that didn't work either. How do I fix this?

答案1

得分: 1

你可以通过通道传递通道来保护所选值的单一访问。只有一个接收器会执行工作,其他接收器在接收到关闭信号后会退出。请参考这个解决方案

英文:

You can send a channel over a channel to protect the single access of a selected value. Only a receiver will do the work and the others will leave when they received the closing signal. See this solution

huangapple
  • 本文由 发表于 2017年1月20日 12:21:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/41756064.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定