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.

Reply via email to