【问题标题】:Stuck with Go concurrencyGo并发卡住了
【发布时间】:2015-05-06 04:29:52
【问题描述】:

我似乎不知道下一步该做什么。我的目标是使用图像包中的 SubImage 函数从原始图像创建所有子图像的数组。我可以在 imageSplit() 函数中对图像进行分区,并通过通道传递给 imageReceiver() 函数。

我实际上在函数 imageReceiver() 中接收数据,但我不知道如何在从 imageSplit() 函数接收到所有图像后附加到数组并使用它。

// Partitions Image
func Partition(src image.Image) []image.Image {

    newImg := image.NewNRGBA64(src.Bounds())

    r := newImg.Rect
    dx, dy := r.Dx(), r.Dy()

   // partitionNum
   pNum := 3

    // partition x
    px, py := (dx / pNum), (dy / pNum)

    imgChan := make(chan image.Image)
    imgStorage := make([]image.Image, 0)

    for i := 1; i < pNum; i++ {
        for j := 1; j < pNum; j++ {
            startX, startY := ((px * i) - px), ((py * j) - py)
            endX, endY := (px * i), (py * j)

            go imageSplit(imgChan, newImg, startX, startY, endX, endY)
            go imageReceiver(imgChan)
        }
    }

    return imgStorage

}

// Creates sub-images of img
func imageSplit(imgChan chan image.Image, img *image.NRGBA64, startX, startY, endX, endY int) {
    r := image.Rect(startX, startY, endX, endY)
    subImg := img.SubImage(r)

    imgChan <- subImg
}

// Receive sub-image from channel
func imageReceiver(imgChan chan image.Image) {
    img := <-imgChan
    spew.Dump(img.Bounds())
}

我想创建一个 image.Image 的全局数组,但我不确定这是否是“保存”所有子图像的正确方法。

我想这有点令人困惑的原因是因为这是我第一次在 Go 中使用并发。 感谢您的帮助:)

【问题讨论】:

  • 你会想要一个sync.WaitGroup,这样Partition 直到goroutines 完成或者在pNum 结果中读取并在Partition 中附加一秒钟才返回。但是我没有看到在这里使用 goroutines 的意义,SubImage 通常很快,因为它通常只创建一个新的图像对象来共享现有图像中的像素数据(即通常没有数据复制,只是一些簿记)。
  • 谢谢,但我在理解如何在我的 imageReceiver() 函数之后创建 image.Image 数组时遇到了问题……@evanmcdonnal 给出了与我的问题相关的答案。感谢 SubImage 函数的附注......我对 Go 并发非常陌生,所以认为这将是同时创建 goroutine 来分割我的图像的完美方式。

标签: go channel goroutine


【解决方案1】:

您有几个选项可以做到这一点,但我想说您的基本问题是您的接收器不进行聚合,如果您更改它,它不会是线程安全的。

修改接收器以进行聚合的简单选择是在循环之前分配一个Image 数组,并将指向它的指针传递给接收器方法,然后在读取通道时只使用附加。但是你会有一堆不同的 goroutine 争夺对同一个数组的访问权。所以真的,你不希望聚合是多线程的。如果是,您需要一个锁定机制才能写入集合。

相反,您想在循环之后阻塞。最简单的方法是将接收器的主体放在循环之后内联;

imgs := []image.Image{}
img := <-imgChan
imgs = append(imgs, img)
spew.Dump(img.Bounds())

问题出在现实世界中,然后您的软件会在那条线上阻塞并且无响应(无法死亡或退出或任何事情),因此您通常会使用至少有 2 个频道的频道选择/案例,Partition 的调用者可以在它需要退出和从 imgChan 接收的案例时使用它来杀死它的中止通道。看起来会更像这样;

imgs := []image.Image{}

select {
    case img := <-imgChan
         imgs = append(imgs, img)
         spew.Dump(img.Bounds())
    case _ := <-abortChan:
        return MyCustomError();
    }

这使得你的聚合不是并发的,只有产生结果的工作,我个人认为这是更好的设计。我也可以解释如何锁定您的接收器方法,但我相信您可以找到很多互斥锁等示例。

【讨论】:

  • 很好,这很有效,我在接收器函数之后无法获取数据......我正计划将 []image.Image 指针传递给接收器函数,但是像你一样说,会有不同的 goroutine 争夺对同一个数组的访问权。感谢 select 语句对我真正的挑战有用,呵呵
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 2012-06-23
  • 2021-10-08
  • 2016-11-24
相关资源
最近更新 更多