为了账号安全,请及时绑定邮箱和手机立即绑定

使用图像包并发的奇怪异常行为

使用图像包并发的奇怪异常行为

Go
拉丁的传说 2022-10-17 16:56:49
我试图开始工作的程序是一维细胞自动图像的生成器,它需要足够强大以处理数百万个单个细胞的订单的超大型模拟,因此多线程图像生成过程是必要的。我之所以选择 Go 是因为 go-routines 将使 CPU 的工作分配问题变得更加容易和高效。现在,因为用单独的 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。



查看完整回答
反对 回复 2022-10-17
  • 1 回答
  • 0 关注
  • 84 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信