Stuck with Go concurrency

Issue

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 🙂

Solution

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.

Answered By – evanmcdonnal

Answer Checked By – David Goodson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.