英文:
Stuck with Go concurrency
问题
我似乎无法弄清楚接下来该做什么。我的目标是使用图像包中的SubImage函数,从原始图像创建一个包含所有子图像的数组。我能够在imageSplit()函数中对图像进行分区,并通过通道将其传递给imageReceiver()函数。
实际上,我在imageReceiver()函数中接收到了数据,但我不知道如何将其附加到数组并在从imageSplit()函数接收到所有图像后使用它。
// 分割图像
func Partition(src image.Image) []image.Image {
newImg := image.NewNRGBA64(src.Bounds())
r := newImg.Rect
dx, dy := r.Dx(), r.Dy()
// 分区数
pNum := 3
// 分区 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
}
// 创建图像的子图像
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
}
// 从通道接收子图像
func imageReceiver(imgChan chan image.Image) {
img := <-imgChan
spew.Dump(img.Bounds())
}
我考虑创建一个全局的image.Image数组,但我不确定这是否是“保存”所有子图像的正确方式。
我想这有点令人困惑,因为这是我第一次在Go中使用并发。感谢任何帮助
英文:
I can't seem to figure out what to do next. My goal is to create an array of all the sub images from the original image using the SubImage function from the image package. I am able to partition an image in the imageSplit() function and pass to imageReceiver() function via a channel.
I actually receive the data in function imageReceiver(), but I don't know how to append to an array and use it after receiving all the images from imageSplit() function.
// 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())
}
I thought of creating a global array of image.Image but I'm unsure if this is the correct way to "save" all the sub images.
I guess the reason this is a bit confusing is because this is the first time I'm working with concurrency in Go.
Thanks for any help
答案1
得分: 1
有几种方法可以解决这个问题,但我认为你的基本问题是接收器不进行聚合,如果你改变它,使其进行聚合,它将不是线程安全的。
修改接收器以进行聚合的简单选择是在循环之前分配一个Image
数组,并将指针传递给接收器方法,当它从通道读取时,只需使用append
。但是,这样会导致许多不同的goroutine争夺对同一数组的访问。所以,实际上,你不希望聚合是多线程的。如果是这样,你需要一种锁机制来写入集合。
相反,你希望在循环之后阻塞。最简单的方法就是将接收器的主体直接放在循环之后,像这样:
imgs := []image.Image{}
img := <-imgChan
imgs = append(imgs, img)
spew.Dump(img.Bounds())
问题是,在现实世界中,你的软件将在那一行上阻塞,并且无法响应(没有任何方式可以终止或退出)。因此,通常会使用通道选择(channel select),其中至少有2个通道/情况,一个是调用Partition
的调用者可以使用的中止通道(abort channel),另一个是从imgChan
接收的情况。代码可能会像这样:
imgs := []image.Image{}
select {
case img := <-imgChan:
imgs = append(imgs, img)
spew.Dump(img.Bounds())
case _ := <-abortChan:
return MyCustomError()
}
这样做可以使你的聚合不是并发的,只有生成结果的工作是并发的,我个人认为这是更好的设计。我也可以解释如何在接收器方法中进行锁定,但我相信你可以找到许多互斥锁(mutex)的示例。
英文:
There are a few options for how you can do this but I would say your basic problem is that your receiver doesn't do aggregation and if you changed it so it did it would not be thread safe.
The simple choice to modify your receiver to do aggregation would be to allocate an Image
array before the loop and pass a pointer to it into the receiver method which would then just use append when it reads of the channel. But then you would have a bunch of different goroutines fighting for access to the same array. So really, you don't want the aggregation to be multithreaded. If it is you need a locking mechanism in order to write to the collection.
Instead you want to block after the loop. The simplest way to do that would just be to put the body of your receiver right there inline after the loop like;
imgs := []image.Image{}
img := <-imgChan
imgs = append(imgs, img)
spew.Dump(img.Bounds())
The problem is in the real world then your software would block on that line and be unresponsive (have no way of dying or exiting or anything) so instead you'd typically use a channel select where you have at least 2 channels/cases, an abort channel that the caller of Partition
can use to kill it if it needs to exit and the case that receives from imgChan
. That would look a little more like this;
imgs := []image.Image{}
select {
case img := <-imgChan
imgs = append(imgs, img)
spew.Dump(img.Bounds())
case _ := <-abortChan:
return MyCustomError();
}
Which make it so your aggregation is not concurrent, only the work to produce the results which I personally think is the better design. I could explain how to lock in your receiver method as well but I'm sure you can find plenty of examples of mutex's ect.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论