-1

To help expand my Go knowledge, I decided to write an iterator for the Collatz Conjecture. Having got it working, I'd now like to make it multi-threaded. I can find various examples of multi-threading a for loop but they appear to be based on a thread per iteration between defined bounds. E.g. for n := range(10) {. In my use case, the upper bound of N is unknown (depending on how long the code is left running). The full code can be seen on Github but, in summary:

n := 0
for {
    stepsToResolve = resolveN(n) //<- "Multithread this"
    n++
}
8
  • Before writing a concurrent program, you need to break a monolithic task into smaller independent ones. Your question doesn't make clear what those independent tasks would be... Commented Aug 8, 2021 at 13:04
  • Hi @jub0bs, sorry, I should have expanded my question a little. I'm probably over-simplifying it. The resolveN function copies N and determines if it is even or odd. If it's odd, it performs (copyN*3)+1. If it's even, copyN/2. This process is repeated until copyN=1. At that time, the code moves on to the next iteration of N. My thinking is that for each value of N, the processing has to be sequential but multiple values of N can be tested simultaneously. Commented Aug 8, 2021 at 13:33
  • You could implement a pattern known as pool of workers, where tasks would simply consists in integers to test next. No more workers than logical processors. Commented Aug 8, 2021 at 13:58
  • See play.golang.org/p/zwE6WnB2VDf to get an idea. Commented Aug 8, 2021 at 14:08
  • @jub0bs, thanks for the pointer. I've found gobyexample.com/worker-pools now and I'll try it out. Your example is even better and specific to my problem. :) Commented Aug 8, 2021 at 14:09

1 Answer 1

2

Your example lacks an exit condition so it's a bit weird. That being said, you don't always know in advance how many subroutines you are about to launch. A typical way of dealing with this is to use sync.WaitGroup. For instance:

for {
        wg.Add(1) // declare new goroutine
        go func(i int, wg *sync.WaitGroup) {
            defer wg.Done() // when work is done, declare termination
            log.Printf("hello wait group %v\n", i)
        }(i, &wg)
        // Please add a stop condition !
    }

wg.Wait() // prevent your main program to return until all goroutines have ended

But in your case it looks like creating thousands of goroutines won't help (you probably have less CPUs available). In that case you can use a pool with limited concurrency instead. If you care to use it, I wrote a library for that:

import "github.com/aherve/gopool"

func main() {
    pool := gopool.NewPool(8) // creates pool with limited concurrency of 8
    for {
        pool.Add(1)
        go func(i int, pool *gopool.GoPool) {
            defer pool.Done()
            time.Sleep(time.Second)
            log.Printf("hello pool %v\n", i)
        }(i, pool)
        // Please add a stop condition !
    }
    pool.Wait()
}

With this version, no more than 8 routines will be launched simultaneously, and the code remains pretty similar to a waitGroup usage.

Sign up to request clarification or add additional context in comments.

2 Comments

you can create an exit condition on top of signals
you might also want to read about pkg.go.dev/golang.org/x/sync/errgroup

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.