Hi, new gopher here. I considered asking this on SO, but they (rightly, IMO) discourage "Is this a good way to do it?" questions. Hope that's ok here.
By way of background, I'm porting a largish industrial control application from Python to Go. The Python version uses multiple processes (about a dozen in all) communicating over ZeroMQ. One process, called the statehouse, controls access to the application state. The others obtain copies and send updates over REQ sockets. The data are serialized as JSON objects that map nicely to Python dicts. Since there's no direct equivalent in Go to a Python dict that can hold a mixture of arbitrary types, I need to use a struct to represent the state. No problem with that but I've been struggling with how to allow the goroutines that will replace the Python processes to read and write to the state struct with concurrency safety. This morning I came up with an idea to send functions over a channel to the main routine. I put together a little test program and after some refinements it looks promising. Some rough benchmarking shows I can get a million updates in under 1 second on a 2012 vintage Mac Mini. That's more than good enough for this application where the time between events is usually more than 100 milliseconds. Here's the link to my test on the Go Playground: https://play.golang.org/p/8iWvwnqBNYl . It runs there except that the elapsed time comes back 0 and the prints from the second goroutine don't show up. I think that's got something to do with the artificial clock in the playground. It works fine when I run it locally. I've pasted the code at the bottom of this message. So my big questions are: - Is this actually concurrency safe as long as all goroutines only use the update mechanism to read and write? - Is there a more idiomatic way to do it that performs as well or better? - What are the potential problems if this is scaled to a couple dozen goroutines? - Does it sacrifice clarity for cleverness? (not that it's all that clever, mind you, but I need to think about handing this off to my client's staff.) Thanks very much, Mike Ellis code follows ... package main import ( "fmt" "time" ) // Big defines the application's state variables type Big struct { A int B string /* and hundreds more */ } // update is a struct that contains a function that updates a Big and // a signal channel to be closed when the update is complete. An update // may also be used to obtain a current copy of a Big by coding f to // do so. (See gopher2 below.) type update struct { done chan struct{} f func(*Big) } // upch is a channel from which main receives updates. var upch = make(chan update) // gopher defines a function that updates a member of a Big and // sends updates via upch. After each send it waits for main to // close the update's done channel. func gopher() { var newA int f := func(b *Big) { b.A = newA } for i := 0; i < n; i++ { newA = i u := update{make(chan struct{}), f} upch <- u <-u.done } } // gopher2 uses an update struct to obtain a current copy of a Big // every 100 microseconds. func gopher2() { var copied Big f := func(b *Big) { copied = *b } for { time.Sleep(100 * time.Microsecond) u := update{make(chan struct{}), f} upch <- u <-u.done fmt.Println(copied) } } // main creates a Big, launches gopher and waits on the update channel. When // an update, u, arrives it runs u.f and then closes u.done. func main() { var state = Big{-1, "foo"} fmt.Println(state) // --> {-1, "foo"} go gopher() go gopher2() start := time.Now() for i := 0; i < n; i++ { u := <-upch u.f(&state) close(u.done) } perUpdate := time.Since(start).Nanoseconds() / int64(n) // Note: always 0 in playground fmt.Printf("%d updates, %d ns per update.\n", n, perUpdate) fmt.Println(state) // --> {n-1, "foo"} } var n = 1000 // number of updates to send and receive -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.