0

Create a WaitGroup by yourself

 2 years ago
source link: https://dannypsnl.github.io/blog/2018/02/15/cs/create-a-wait-group-by-yourself-and-do-not-use-it/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Create a WaitGroup by yourself

If you had written any concurrency code in Go, I think you could seem sync.WaitGroup before. Today's we will focus on create a wait group by channel trick.

How? First, you need a channel without buffer.

func main() {
    wait := make(chan struct{})
}

Then get something from it so if there has no value in wait, process keep going. But if just trying to get something from a empty channel. You will get block and cause deadlock. Then go will panic it. So we have to change the code a little bit but also be more extendable for next iteration.

// ...
n := 0
wait := make(chan struct{})

for i := 0; i < n; i++ {
    <-wait
}
// ...

Now let's create works loop.

import (
    "time"
)
// ...
n := 10000
wait := make(chan struct{})

for i := 0; i < n; i++ {
    time.Sleep(time.Microsecond)
    wait <- struct{}{}
}

for i := 0; i < n; i++ {
    <-wait
}
// ...

ps. These code has a little bug(you can try to find it, or read the answer at the end)

Now we can see it work. The reason of these code can work is because size n is our expected amount of workers. After each worker done their jobs. They will send something(here is struct{}{}, but exactly is doesn't matter thing) into our wait channel. We only read n things from wait.

So after n things be read. We won't be blocked anymore even wait got new thing. Else we have to waiting wait.

Whole code dependent on this fact. Having this knowledge, we can create ours WaitGroup now.

As you can see, we use a type wrapping all the thing we need.(It's a basic idiom, so I don't want to say why)

Then method Add is preparing for n we talk before. Adding these things in dynamic way. Next Done do the thing as we manually do in previous code, and Wait is read amount of things equal final n. Let's say what happened in previous code. You should close the channel always. So the code will be:

// ...
wait := make(chan interface{})
defer close(wait)
// ...

Maybe you will feel confusing about this part. The reason is channel won't be collect by GC automatic(if it can, it will be another hell). So always closing it is important.

ps. In productive code, please still using the sync.WaitGroup, I do a test, sync.WaitGroup is 25% faster than the version you see at here.

References:

The Go programming language

  • Author: Alan A. A. Donovan & Brian W. Kernighan
  • ISBN: 978-986-476-133-3

Concurrency in Go

  • Author: Katherine Cox-Buday
  • ISBN: 978-1-491-94119-5

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK