我試圖開始工作的程序是一維細胞自動圖像的生成器,它需要足夠強大以處理數百萬個單個細胞的訂單的超大型模擬,因此多線程圖像生成過程是必要的。我之所以選擇 Go 是因為 go-routines 將使 CPU 的工作分配問題變得更加容易和高效?,F在,因為用單獨的 go-routine 編寫每個單元格根本不會非常高效,我決定創建一個函數來調用圖像對象并負責生成一整行單元格。此函數引用包含位切片的 2D 數組對象(請參閱此) 要繪制的所有單元格的數組,因此有許多循環,但這對于手頭的問題并不重要。程序應該做的是簡單地讀取所有單獨的位并將一個正方形寫入圖像矩形的正確位置,表示存在一個單元格(基于變量 pSize 表示正方形的邊長)。這是那個功能...func renderRow(wg *sync.WaitGroup, img *image.RGBA, i int, pSize int) { defer wg.Done() var lpc = 0 for j := 0; j < 64; j++ { for k := range sim[i] { for l := lpc * pSize; l <= (lpc*pSize)+pSize; l++ { for m := i * pSize; m <= (i*pSize)+pSize; m++ { if getBit(sim[i][k], j) == 1 { img.Set(l, m, black) } else { img.Set(l, m, white) } } } lpc++ } }}現在我很高興地說,當在一個線程上按順序運行時,這里的這個函數的性能和預期的一樣。這是非并行函數調用(忽略等待組)img = image.NewRGBA(image.Rectangle{Min: upLeft, Max: lowRight})for i := range sim { renderRow(&wg, img, i, pSize)}f, _ := os.Create("export/image.png")_ = png.Encode(f, img)另一方面,當我們對并發實現進行簡單更改時,輸出有幾個單獨的像素錯誤,并且似乎隨著每次運行的錯誤數量的變化而隨機收縮和擴展某些行。這是并發函數調用。這是并發函數調用...img = image.NewRGBA(image.Rectangle{Min: upLeft, Max: lowRight})for i := range sim { go renderRow(&wg, img, i, pSize) // TODO make multithreaded again}wg.Wait()f, _ := os.Create("export/image.png")_ = png.Encode(f, img)現在這兩個各自實現的輸出是什么樣的?使用這些起始條件{0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1}和11(pSize 2) 的演化空間。我們將其作為單線程實現的輸出...現在,如果您放大該圖像,您會發現所有正方形都垂直和水平均勻分布,沒有異常。然而,現在讓我們看看并發輸出。這個版本似乎有幾個異常,很多行都被縮小了,很多地方都有個別像素錯誤,雖然它正確地遵循了模擬的一般模式,但它肯定在視覺上并不令人愉悅。當我在調查這個問題時,我尋找與并發相關的問題,所以我認為圖像包中像素數組的動態分配可能會導致某種沖突,所以我調查img.Set()了看起來像這樣的沖突......
1 回答

阿波羅的戰車
TA貢獻1862條經驗 獲得超6個贊
上面的代碼產生了許多競爭沖突,這些沖突是由于 go-routines 試圖寫入 .Pix 對象中的相同像素坐標而引起的。修復在renderRow
函數內,其中當前像素的寬度和高度的計算在每次迭代中由于<=
而不是“<”而重疊。故事的寓意是用于-race
查找沖突并始終查找相同變量的覆蓋或并發讀取。感謝@rustyx。
- 1 回答
- 0 關注
- 106 瀏覽
添加回答
舉報
0/150
提交
取消