I have to update thousands of structs with datas available on some remote servers. So, I have to deal with thousands of Goroutines querying these remote servers (http requests or db requests) to update the struct with the responses. But the update (or not) of the struct is depending of the results of other structs.
So I imagined a simple code in which the goroutines are running, each of them performs its own request, put the result in a global struct that contain any information retrieved by the goroutines and informs the main func that the first part of the job is done (waiting for the signal that every goroutines do the same before deciding of updating or not its struct.)
The goroutine should now waits for a signal of the main thread that all goroutines are done before deciding updating or not
the simplified code looks like that :
type struct StructToUpdate {
//some properties
}
type struct GlobalWatcher {
//some userful informations for all structs to update (or not)
}
func main() {
//retrieving all structs to update
structsToUpdates := foo()
//launching goroutines
c := make(chan bool)
for _,structToUpdate := range structsToUpdates {
go updateStruct(&structToUpdate,c)
}
//waiting for all goroutines do their first part of job
for i:=0; i<len(structsToUpdates); i++ {
<- c
}
//HERE IS THE CODE TO INFORM GOROUTINES THEY CAN RESUME
}
func updateStruct(s *StructToUpdate, c chan bool) {
result := performSomeRequest(s)
informGlobalWatcherOfResult(result)
c <- true //I did my job
//HERE IS THE CODE TO WAIT FOR THE SIGNAL TO RESUME
}
The question is : What the more performant / idiomatic / elegant wait to :
- send a signal from the main script ?
- wait for this signal from the goroutine ?
I can imagine 3 ways to do this
- in a first way, I can imagine a global bool var
resume := falsethat will be turned totrueby the main func when all goroutines do the first part of job. In this case, each goroutine can use an uglyfor !resume { continue }.... - in a more idiomatic code, I can imagine do the same thing but instead of using a bool, I can use a
context.WithValue(ctx, "resume", false)and pass it to goroutine, but I still have afor !ctx.Value("resume") - in a last more elegant code, I can imagine using another
resume := make(chan bool)passed to the goroutine. The main func could inform goroutines to resume closing this chan with a simpleclose(resume). The goroutine will wait the signal with something than :
for {
_, more := <-resume
if !more {
break
}
}
//update or not
Is there any other good idea to do this ? One of the above solutions is better than others ?
I'm not sure if I understand your question completely, but a simple solution to blocking the main thread is with an OS signal ie.:
This doesn't have to be an OS signal, this could be a channel that receives a
struct{}{}value from somewhere else in your runtime.If you need to do this as a result of multiple go routines working, I'd look into using
sync.WaitGroup.