我是Golang并發的新手,一直在努力理解下面提到的這段代碼。
我目睹了一些我無法解釋為什么會發生的事情:
在 main 函式中使用小于等于 100000 的 i 時
for i <= 100000 {,它有時會為 nResults 和 countWrites 列印不同的值(在最后兩個陳述句中)fmt.Printf("number of result writes %d\n", nResults) fmt.Printf("Number of job writes %d\n", jobWrites)當我使用超過 1000000 時,它給出
panic: send on closed channel
我如何確保發送給作業的值不在關閉的通道上,并且稍后在結果中收到所有值后我們可以關閉通道而不會出現死鎖?
package main
import (
"fmt"
"sync"
)
func worker(wg *sync.WaitGroup, id int, jobs <-chan int, results chan<- int, countWrites *int64) {
defer wg.Done()
for j := range jobs {
*countWrites = 1
go func(j int) {
if j%2 == 0 {
results <- j * 2
} else {
results <- j
}
}(j)
}
}
func main() {
wg := &sync.WaitGroup{}
jobs := make(chan int)
results := make(chan int)
var i int = 1
var jobWrites int64 = 0
for i <= 10000000 {
go func(j int) {
if j%2 == 0 {
i = 99
j = 99
}
jobWrites = 1
jobs <- j
}(i)
i = 1
}
var nResults int64 = 0
for w := 1; w < 1000; w {
wg.Add(1)
go worker(wg, w, jobs, results, &nResults)
}
close(jobs)
wg.Wait()
var sum int32 = 0
var count int64 = 0
for r := range results {
count = 1
sum = int32(r)
if count == nResults {
close(results)
}
}
fmt.Println(sum)
fmt.Printf("number of result writes %d\n", nResults)
fmt.Printf("Number of job writes %d\n", jobWrites)
}
uj5u.com熱心網友回復:
您的代碼中有很多問題。
在關閉的頻道上發送
使用 Go 通道的一個一般原則是
不要從接收端關閉通道,如果通道有多個并發發送者則不要關閉通道
( https://go101.org/article/channel-closing.html )
您的解決方案很簡單:不要有多個并發發件人,然后您可以從發件人端關閉頻道。
與其為添加到通道的每個作業啟動數百萬個單獨的 goroutine,不如運行一個執行整個回圈的 goroutine 以將所有作業添加到通道。并在回圈后關閉通道。作業人員將盡可能快地使用通道。
通過修改多個 goroutine 中的共享變數來進行資料競爭
您在不采取特殊步驟的情況下修改兩個共享變數:
nResults,您將其傳遞給countWrites *int64作業人員中的。i在寫入作業通道的回圈中:您從多個 goroutines 向它添加 99,這使得它無法預測您實際向jobs通道寫入了多少值
要解決 1,有很多選項,包括使用sync.Mutex. 但是,由于您只是添加它,所以最簡單的解決方案是使用atomic.AddInt64(countWrites, 1)而不是*countWrites = 1
要解決 2,不要在每次寫入通道時使用一個 goroutine,而是在整個回圈中使用一個 goroutine(見上文)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/535728.html
標籤:去并发
上一篇:如何退出具有通道無限回圈的功能
