Reputation: 15760
I'm a newbie to Go & I'm writing a simple web application. The basic idea is this:
goroutine #1 is the HTTP server
goroutine #2 is a function which manages the state of the application. It needs to poll some hardware for values periodically, so it needs to run in its own thread.
When a user makes an HTTP request, the server will request updated information from the state goroutine, then return an HTML page to the client.
It all works very well except for one thing: there are currently six different possible signals sent by the server and returned from the state thread, with both a request and a response channel, for a total of 12 channels (and I'm going to be adding at least one or two more signals). It may be possible to consolidate some of the signals, but many of them are different types.
As I said, it works, but it's getting rather ugly.
It looks something like this:
main.go includes declarations to instantiate the channels:
// a shutdown signal
shutdown := make(chan bool)
// request the current state of a sensor
sensorRequest := make(chan string)
sensorResponse := make(chan Sensor)
// request the current state of ALL sensors
sensorListRequest := make(chan bool)
sensorListResponse := make(chan []Sensor)
// request the current state of the application (active or inactive)
statusRequest := make(chan bool)
statusResponse := make(chan bool)
// and a few more...
// start the "state manager" goroutine
go ManageState(shutdown, sensorRequest, sensorResponse, sensorListRequest, sensorListResponse, statusRequest, statusResponse)
// start the "HTTP server" goroutine
go ManageHTTP(shutdown, sensorRequest, sensorResponse, sensorListRequest, sensorListResponse, statusRequest, statusResponse)
So, a couple of questions:
is it necessary to have separate request & response channels? Given that the request and response are different types, I can't see an obvious way to use the same channel in both directions (that isn't hacky).
I considered putting all the channels into a struct which could be passed to all the goroutines, but I'm not sure that's going to be any real benefit - it doesn't seem much clearer than what I'm doing now.
What is the Go way™ of doing this?
Update
Thanks to @kostya and @Aedolon for suggesting a global state with sync.Mutex
for locking/unlocking. That was actually my first approach, but I ran into a serious problem: my Mutex Lock()
calls were randomly failing to unblock when the lock was released. This code will be running on a Raspberry Pi, and I suspect there's a bug somewhere (either in the package I'm using to read the hardware state or in the Go ARM binaries). But after spending a bunch of time trying to trace the problem, I decided to try something different.
At any rate, after reading (many times) that I should "not communicate by sharing memory; instead, share memory by communicating" (The Go Blog), I decided to attempt to implement it this way. And now that I'm doing it this way, I find that I like this approach :-)
Upvotes: 2
Views: 654
Reputation: 9559
I would not use channels for this at all.
You can update and read shared state from the same variable from multiple go-routines and use sync.RWMutex for synchronization.
Upvotes: 1