I am new to the Golang, please bear me and kindly clarify my query.
what I understood from wg.Done() call, this is an indication to WaitGroip that my goroutine is done so that wait call by WaitGroup will end, let's consider a scenario where we have only 1 goroutine running, So generally we use defer keyword for wg.Done() so it will get called at the end of the goroutine block.
I came across below code snippet, where wg.Done() it's being called in the midway, I am confused why it's being called here and also when I practiced code using wg.Done() in the midway, code after that it's not been called, So with that in mind I am confused in the below codebase wg.Done() is being called in the mid-way.
func startNetworkDaemon() *sync.WaitGroup {
var wg sync.WaitGroup
wg.Add(1)
go func() {
connPool := warmServiceConnCache()
server, err := net.Listen("tcp", "localhost:8080")
if err != nil {
log.Fatalf("cannot listen: %v", err)
}
defer server.Close()
wg.Done()
for {
conn, err := server.Accept()
if err != nil {
log.Printf("cannot accept connection: %v", err)
continue
}
svcConn := connPool.Get()
fmt.Fprintln(conn, "")
connPool.Put(svcConn)
conn.Close()
}
}()
return &wg
}
Another code sample as well, in the below code if add defer keyword to the wg.Done() method, I am getting a deadlock error. Can someone explain the reason for this...
func usingBroadcast() {
beeper := sync.NewCond(&sync.Mutex{})
var wg sync.WaitGroup
wg.Add(1)
miniFunc(func() {
fmt.Println("mini1")
wg.Done()
}, beeper)
beeper.Broadcast()
wg.Wait()
}
func miniFunc(fn func(), beeper *sync.Cond) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
wg.Done()
beeper.L.Lock()
fmt.Println("i am waiting")
beeper.Wait()
beeper.L.Unlock()
fn()
}()
wg.Wait()
}
CodePudding user response:
A waitgroup waits until all that are added into the group are done. Those things in the waitgroup can be goroutines, or something else. In the first code snippet, it looks like the waitgroup is there to capture the state where the goroutine completed initialization. Once the goroutine calls the net.Listen, it notifies the waiting goroutine that the initialization is complete.
In the second example, there is a potential wait, and the waitgroup is notified before the wait. It looks like a scheme to make sure that before the waitgroup is notified the goroutine started running.
CodePudding user response:
The wait group is not needed in the examples.
If you look at the caller of startNetworkDaemon, you will find that it immediately calls Wait on the returned wait group. The caller is basically waiting on the creation on of the listener. Here's a refactoring that accomplishes the same goal:
func startNetworkDaemon() {
connPool := warmServiceConnCache()
server, err := net.Listen("tcp", "localhost:8080")
if err != nil {
log.Fatalf("cannot listen: %v", err)
}
go func() {
defer server.Close()
for {
conn, err := server.Accept()
if err != nil {
log.Printf("cannot accept connection: %v", err)
continue
}
svcConn := connPool.Get()
fmt.Fprintln(conn, "")
connPool.Put(svcConn)
conn.Close()
}
}()
}
Because goroutine does not do anything before calling Done in the miniFunc
example, the wait group serves no purpose. Refactor by removing the wait group.
func miniFunc(fn func(), beeper *sync.Cond) {
go func() {
beeper.L.Lock()
fmt.Println("i am waiting")
beeper.Wait()
beeper.L.Unlock()
fn()
}()
}