I was reading through the Go concurrency book, and I came across the following example.
func condVarBroadcast() {
type button struct {
clicked *sync.Cond
}
b := button{sync.NewCond(&sync.Mutex{})}
// for each call to subscribe, start up a new goroutine
// and wait using the condition variable.
subscribe := func(c *sync.Cond, fn func()) {
var w sync.WaitGroup
w.Add(1)
go func() {
w.Done()
c.L.Lock()
defer c.L.Unlock()
c.Wait()
fn()
}()
w.Wait()
}
var wg sync.WaitGroup
wg.Add(3)
subscribe(b.clicked, func() {
fmt.Println("Maximize window")
wg.Done()
})
subscribe(b.clicked, func() {
fmt.Println("displaying dialog box")
wg.Done()
})
subscribe(b.clicked, func() {
fmt.Println("mouse clicked")
wg.Done()
})
b.clicked.Broadcast()
wg.Wait()
}
I had a couple of doubts:
if I replace "w.Done()" with "defer w.Done()", there is a deadlock. Why? What is the point of doing w.Done() inside a goroutine when it clearly is not done executing? Is this a normal practice?
Why do we even need to waitgroup w in this example? If I don't use it, it says "deadlock: all goroutines are asleep.
Here the problem with race condition is that the main is super fast and broadcasts to the sync.Cond even before the go-routines start and wait for the lock. The reason to have w waitgroup is to make sure that all the go-routines start first and hold the lock before the main thread signals the broadcast
func condVarBroadcast() {
type button struct {
clicked *sync.Cond
}
b := button{sync.NewCond(&sync.Mutex{})}
// for each call to subscribe, start up a new goroutine
// and wait using the condition variable.
subscribe := func(c *sync.Cond, fn func()) {
var w sync.WaitGroup
w.Add(1)
go func() {
w.Done() // <-------- this will notify that the goroutine has started
c.L.Lock()
defer c.L.Unlock()
c.Wait()
fn()
}()
w.Wait() // <--- this will block till the go routine starts and then continue
}
var wg sync.WaitGroup
wg.Add(3)
subscribe(b.clicked, func() {
fmt.Println("Maximize window")
wg.Done()
}) // < ----- This call will block till the go routine starts
subscribe(b.clicked, func() {
fmt.Println("displaying dialog box")
wg.Done()
}) // < ----- This call will block till the go routine starts
subscribe(b.clicked, func() {
fmt.Println("mouse clicked")
wg.Done()
}) // < ----- This call will block till the go routine starts
b.clicked.Broadcast() // <------------ Once this happens all the go routines will continue the processing
wg.Wait()
}
If you want to avoid the w waitgroup, you have to somehow make sure the Broadcast call happens after all the go routines start in that case you could add a simple time.Sleep(time.Second * 1) before the Broadcast call and remove the w waitgroup from the code.
Related
Let's suppose client uploads heavy image by http with slow internet connection, he opens connection to server, starts to write data and suddenly he loses this connection because of network problems. How can I detect this on server if handler function was not yet called. The solution I found is to check the connection state. But the problem is that it's not scalable, because a lot of goroutines will interact with global variable. Are there any more elegant solutions?
package main
import (
"fmt"
"log"
"net"
"net/http"
)
// current state of connections
var connStates = make(map[string]http.ConnState)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("got new request")
fmt.Println(connStates)
})
srv := &http.Server{
Addr: ":8080",
ConnState: func(conn net.Conn, event http.ConnState) {
if event != http.StateClosed {
connStates[conn.RemoteAddr().String()] = event
} else {
if connStates[conn.RemoteAddr().String()] == http.StateActive {
fmt.Println("client cancelled request")
}
delete(connStates, conn.RemoteAddr().String())
}
},
}
log.Fatal(srv.ListenAndServe())
}
You could use context within your handler, for example, this would detect when the client disconnects and return and http.StatusPartialContent besides calling someCleanup() in where you could have your logging logic.
https://play.golang.org/p/5Yr_HBuyiZW
func helloWorld(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ch := make(chan struct{})
go func() {
time.Sleep(5 * time.Second)
fmt.Fprintln(w, "Hello World!")
ch <- struct{}{}
}()
select {
case <-ch:
case <-ctx.Done():
http.Error(w, ctx.Err().Error(), http.StatusPartialContent)
someCleanUP()
}
}
If you only need to have logs you can even simplify the code:
srv := &http.Server{
Addr: ":8080",
ConnState: func(conn net.Conn, event http.ConnState) {
log.Printf("addr: %s, changed state to: %s", conn.RemoteAddr(), event.String())
},
}
That callback will be triggered on each change of the conn
I am following the https://blog.golang.org/pipelines article to implement a few stages.
I need one of the stages to introduce a delay of a few seconds before the events are passed on the next stage in the pipeline.
My concern with the code below is that it will yield an unbounded number of go routines that time.Sleep() before passing the events along. Are there better ways to do this?
Thanks!
func fooStage(inChan <- chan *Bar) (<- chan *Bar) {
out := make(chan *Bar, 10000)
go func() {
defer close(out)
wg := sync.WaitGroup{}
for {
select {
case event, ok := <-inChan:
if !ok {
// inChan closed
break
}
wg.Add(1)
go func() {
time.Sleep(5 * time.Second)
out <- event
wg.Done()
}()
}
}
wg.Wait()
}()
return out
}
You could use another channel to limit the number of active goroutines your loop is able to create.
const numRoutines = 10
func fooStage(inChan <-chan *Bar) <-chan *Bar {
out := make(chan *Bar, 10000)
routines := make(chan struct{}, numRoutines)
go func() {
defer close(out)
wg := sync.WaitGroup{}
for {
select {
case event, ok := <-inChan:
if !ok {
// inChan closed
break
}
wg.Add(1)
routines <- struct{}{}
go func() {
time.Sleep(5 * time.Second)
out <- event
wg.Done()
<-routines
}()
}
}
wg.Wait()
}()
return out
}
You may fix the number of goroutines manually - starting only number you need.
func sleepStage(in <-chan *Bar) (out <-chan *Bar) {
out = make(<-chan *Bar)
wg := sync.WaitGroup
for i:=0; i < N; i++ { // Number of goroutines in parallel
wg.Add(1)
go func(){
defer wg.Done()
for e := range in {
time.Sleep(5*time.Seconds)
out <- e
}
}()
}
go func(){}
wg.Wait()
close(out)
}()
return out
}
You can use time.Ticker:
func fooStage(inChan <- chan *Bar) (<- chan *Bar) {
//... some code
ticker := time.NewTicker(5 * time.Second)
<-ticker // the delay, probably need to call twice
ticker.Stop()
close(ticker.C)
//... rest code
}
This is what you should use for a pipeline application. The context allows for faster tear down.
Whatever is responsible for managing your in channel must close it during tear down. Always close your channels.
// Delay delays each `interface{}` coming in through `in` by `duration`.
// If the context is canceled, `in` will be flushed until it closes.
// Delay is very useful for throtteling back CPU usage in your pipelines.
func Delay(ctx context.Context, duration time.Duration, in <-chan interface{}) <-chan interface{} {
out := make(chan interface{})
go func() {
// Correct memory management
defer close(out)
// Keep reading from in until its closed
for i := range in {
// Take one element from in and pass it to out
out <- i
select {
// Wait duration before reading from in again
case <-time.After(duration):
// Don't wait if the context is canceled
case <-ctx.Done():
}
}
}()
return out
}
I have already solved such a problem with my pipeline library, like that:
import "github.com/nazar256/parapipe"
//...
pipeline := parapipe.NewPipeline(10).
Pipe(func(msg interface{}) interface{} {
//some code
}).
Pipe(func(msg interface{}) interface{} {
time.Sleep(3*time.Second)
return msg
}).
Pipe(func(msg interface{}) interface{} {
//some other code
})
I would like to know how to properly implement/use context.Done() method when using it within an HTTP server and implementing middleware, my goal is to cancel subsequent events when a client disconnects across nested middleware.
For testing I created the following code, I don't know if is the correct way of doing it since I had to create a channel within the HandleFunc and a goroutine to handle the requests, putting all this together within a select wait statement.
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func hello(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log.Println("handler started")
defer log.Println("hander ended")
ch := make(chan struct{})
go func() {
time.Sleep(5 * time.Second)
fmt.Fprintln(w, "Hello")
ch <- struct{}{}
}()
select {
case <-ch:
case <-ctx.Done():
err := ctx.Err()
log.Println(err)
http.Error(w, err.Error(), http.StatusPartialContent)
}
}
func main() {
http.HandleFunc("/", hello)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Basically here the request simulates load by sleeping 5 seconds, and then prints Hello, but if the client cancels the request, for example:
$ curl 0:8080
And then pressing ctl + c, this will be loged:
2017/07/07 22:22:40 handler started
2017/07/07 22:22:42 context canceled
2017/07/07 22:22:42 hander ended
This works but wondering if this pattern (the goroutine and select) should be used in every nested handler or if there is a better way of implementing this.:
ch := make(chan struct{})
go func() {
// some logic
ch <- struct{}{}
}()
select {
case <-ch:
case <-ctx.Done():
err := ctx.Err()
log.Println(err)
http.Error(w, err.Error(), http.StatusPartialContent)
}
At Google, we require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests.
-- Go Concurrency Patterns: Context
I'm trying to write a http server which when received terminate signal, it will wait until all requests has been responsed then close with code below
// start server
var wg sync.WaitGroup
server := &http.Server{
ConnState: func(conn net.Conn, state http.ConnState) {
switch state {
case http.StateNew:
wg.Add(1)
case http.StateClosed:
wg.Done()
}
},
}
go func(server *http.Server, listener net.Listener, terminate chan<- int) {
var status int
if err := server.Serve(listener); err != nil {
if !strings.Contains(err.Error(), "use of closed network connection") {
log.Println(err)
status = 1
} else {
status = 0
}
} else {
status = 0
}
log.Printf("performing graceful close on all clients\n")
wg.Wait()
terminate <- status
}(server, listener, terminate)
but when I call listener.Close() it stuck at wg.Wait() even though all request has already been response. And while it wait, if I send another request, it still give a response correctly.
As i learned from golang docs, if i set runtime.GOMAXPROCS(8) with a cpu of 8 cores (intel i7), then start a goroutine of infinite-loop, other gorutines should not be blocked because there are engough threads and goprocs. But this is not true when using net/http package, an infinite-loop goroutine will block http server after a few invocations.
Can anyone help to explain why ?
If i comment the line of "go infinite loop", start client after server, client will output 1000 asterisks; but if i enable the goroutine, client will block after print a few asterisks
I have tried add runtime.LockOSThread() in the goroutine, it seems that doesn't work
My Environment: osx 10.10, go version go1.3.1 darwin/amd64
Server code:
package main
import (
"fmt"
"log"
"net/http"
"runtime"
)
func myHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello"))
}
func infiniteloop() {
for {
}
}
func main() {
// set max procs for multi-thread executing
runtime.GOMAXPROCS(runtime.NumCPU())
// print GOMAXPROCS=8 on my computer
fmt.Println("GOMAXPROCS=", runtime.GOMAXPROCS(-1))
http.Handle("/", http.HandlerFunc(myHandler))
// uncomment below line cause server block after some requests
// go infiniteloop()
if err := http.ListenAndServe(":8280", nil); err != nil {
log.Fatal(err)
}
}
Client code:
package main
import (
"fmt"
"net/http"
)
func getOnce() {
if resp, err := http.Get("http://localhost:8280"); err != nil {
fmt.Println(err)
return
} else {
defer func() {
if err := resp.Body.Close(); err != nil {
fmt.Println(err)
}
}()
if resp.StatusCode != 200 {
fmt.Println("error codde:", resp.StatusCode)
return
} else {
fmt.Print("*")
}
}
}
func main() {
for i := 1; i < 1000; i++ {
getOnce()
if i%50 == 0 {
fmt.Println()
}
}
}
Now i know why such emtpy loop block other goroutines, but why runtime.LockOSThread() doesn't help either?
func infiniteloop() {
// add LockOSThread will not help
runtime.LockOSThread()
for {
}
}
As http://golang.org/pkg/runtime/#LockOSThread mentioned, the empty loop should be executed in an standalone thread, and other goroutines should not be impacted by the busy loop. What's wrong in my understanding?
The Go runtime's scheduler is not fully pre-emptive at this time. Go 1.2 improved matters by occasionally calling into the scheduler on function calls, but the infinite loops in your example have no function calls so this doesn't help.
With an actual body to your infinite loop handlers, you may see better behaviour. Alternatively, a manual call to runtime.Gosched may help in cases like this.
The scheduler might not be able to preempt such an empty "infinite" loop. The scheduler got better and better during the last release, maybe he should be good enough for such code; he definitely is good enough for real code. Just don't do such nonsense.