简体   繁体   中英

Best way to implement trackable concurrency in Golang

So I've been writing a small toy program to run simple tasks like Jenkins does. Some features this program has are:

  • in-memory job queue.
  • Executor(s) (I can specify how many jobs I want to run concurrently from the queue using a CLI flag.)
  • Can add and cancel jobs (Both in the queue and when its running.) via REST API.

I've been following this tutorial using goroutines to pull stuff out of a channel and process them. However I keep running into the problem of how would I implement canceling of certain goroutines that already processing a job. For example:

I have 5 jobs in a queue and 2 executors (Executor A and Executor B). Both executors pull one job off the queue and process. However, I want to have Executor B stop its processing of it's current job and pull the next job off the queue.

It seems that goroutines can't really be tracked in terms of "goroutine A... B... C..." and it seems not to be best practice to implement that sort of logic. With that being said is there any pattern out there that would solve my use case?

One of the ways to cancel processing in a goroutine is to use a cancelable context: context.WithCancel . That gives you a context and a cancel function which you can use to notify a goroutine that it is canceled. The goroutine should periodically check for the context and terminate if it is canceled.

You can track goroutines using their contexts. For each goroutine you create, you can create a cancelable context, and keep the context and the cancel function in a list/map that holds your running tasks. Then your goroutines will look like this:

func executor(ctx context.Context) {
  for {
     // Do stuff
     select {
       case <-ctx.Done():
           // terminate
       default:
     }
    // Do more stuff
  }
}

You have to poll the context every now and then to see if it was canceled.

And you start a goroutine like this:

go func() {
   ctx, cancel:=context.WithCancel(context.Background())
   defer cancel()
   // Store ctx and cancel in a map if you need to
   executor(ctx)
   // Remove stored context
}()

When you need to cancel a running goroutine, call its cancel function, and it'll terminate eventually.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM