A map[string]interface{} can hold an arbitrary, string labeled collection 
of any types. You just need to have a type switch and cover all the 
possible inputs for it.

The concurrency is safe as long as only one process is writing to variables 
at once. If you need multiple concurrent processes to be able to write then 
you may need to consider what 'atomic' is in the system ensure there is 
mutex sync locks on them so you don't get writes changing data while 
another thread is accessing it. Putting sequence numbers on records to 
indicate the revision count also can help ensure you can resolve conflicts.

On Tuesday, 25 September 2018 20:39:07 UTC+2, Michael Ellis wrote:
>
> 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