gologic

Execute task based on dependencies in Go


I think this is more of a question about logic than Go itself. I want to find a way to execute a task based on its dependencies.

Task Workflow

Considering the workflow above: Task 1,2,3 and 4 can be ran asynchronously at the same time. Task 10 will be executed when Task 1 and 2 are finished. Task 11 will be executed when Task 3 and 4 are finished Task 12 will be executed when Task 11 is finished Task 100 will be executed when Task 10 and 11 are finished.

I'm using Go Channels to make the concurrent executions and want to find an effective way to control the dependencies.

I know I could have some sort of flag and a table to control execution but ideally I would like to do it in memory to avoid the database calls to control such thing. I also understand that there are several ways of doing this, but would like to hear your ideas because I'm sure there is a better way of doing than the ways I could come up with so far.


Solution

  • There was an interesting reddit thread on that topic, in response to the article "How to Wait for All Goroutines to Finish Executing Before Continuing".

    The correct article is "How to Wait for All Goroutines to Finish Executing Before Continuing, Part Two: Fixing My Oops" and illustrates several ways to wait for goroutines before carrying on with another task.

    Depending on the kind of information you have for the tasks to synchronize, sync.WaitGroup is a good candidate (as in this example).

    But:

    When you know the number of messages to expect you might as well count them to know when to finish. Here the WaitGroup is superfluous and confusing.

    This would block until all 3 messages are received:

    func main() {
      messages := make(chan int)
      go func() {
          time.Sleep(time.Second * 3)
          messages <- 1
      }()
      go func() {
          time.Sleep(time.Second * 2)
          messages <- 2
      }()
      go func() {
          time.Sleep(time.Second * 1)
          messages <- 3
      }()
      for i := 0; i < 3; i++ {
          fmt.Println(<-messages)
      }
    }
    

    So it really depends on what you know from the tasks you are waiting for.