1. Use channel s to wait for the task to finish
Used in many places in the previous content:
time.Sleep(time.Millisecond)
For example:
func chanDemo(){ var channels [10]chan int //Create channel Array for i := 0; i < 10; i++{ channels[i] = creatworker(i) } for i := 0; i < 10; i++{ channels[i] <- 'a' + 1 } time.Sleep(time.Millisecond) }
func bufferedChannel(){ ch := make(chan int, 3) go worker(0, ch) for i := 0; i < 10; i++{ ch <- 'a' + i } time.Sleep(time.Millisecond) }
func channelClose(){ ch := make(chan int) go worker(0, ch) ch <- 'a' ch <- 'b' ch <- 'c' ch <- 'd' close(ch) time.Sleep(time.Millisecond) }
It's easy to know how long they take to run, but in fact, many programs take an unpredictable amount of time. We can't always use time packages to predict how long a program will run. It's unreasonable, so here we have channel to wait for the task to finish.
First look at this code:
func chanDemo(){ var channels [10]chan int //Create channel Array for i := 0; i < 10; i++{ channels[i] = creatworker(i) } for i := 0; i < 10; i++{ channels[i] <- 'a' + 1 } time.Sleep(time.Millisecond) }
We need to use channel to print 10 letters concurrently. In order to print the letters completely, we have estimated how long the program will run and let it run for 1 millisecond to finish. Here we need to use
1. Use channel s to wait for the task to finish
Still print letters (20 prints): see Code Notes for some content
Use'chan bool'channel to share communication to use memory and tell main task to end
package main import ( "fmt" ) //Define a structure //done with an in and a bool of'chan int' type Worker struct{ in chan int done chan bool //Receive and receive done as end signal } func DoWork( id int, c chan int, done chan bool){ for { n := <-c //Accept channel content fmt.Printf("worker %d received %c\n", id, n) done <- true //done receives data true } } func createWorker(id int) Worker { //Create an object w for Worker w := Worker{make(chan int), make(chan bool), } go DoWork(id, w.in, w.done) Start here goroutine,Immediate concurrency return w } func ChanDemo() { var workers [10]Worker //Create 10 abstract types of Worker s for i := 0; i < 10; i++{ workers[i] = createWorker(i) //Create 10 Worker objects and return them to workers[i] } for i := 0; i < 10; i++{ //range can be used workers[i].in <- 'a' + i //workers[i].in accepts data } for _, worker := range workers { <-worker.done //Send done's data to mian to tell main that the task is over } for i := 0; i < 10; i++{ //range can be used workers[i].in <- 'A'+ i //workers[i].in accepts data } for _, worker := range workers{ <- worker.done //Send done's data to mian to tell main that the task is over } } func main(){ ChanDemo() }
Print results:
worker 0 received a
worker 5 received f
worker 1 received b
worker 6 received g
worker 4 received e
worker 9 received j
worker 8 received i
worker 2 received c
worker 7 received h
worker 3 received d
worker 6 received G
worker 2 received C
worker 3 received D
worker 7 received H
worker 1 received B
worker 4 received E
worker 5 received F
worker 0 received A
worker 9 received J
worker 8 received I
As you can see from the printed results, they are printed in order (lowercase before uppercase)
Here's another way:
func ChanDemo() { var workers [10]Worker for i := 0; i < 10; i++{ workers[i] = createWorker(i) } for i := 0; i < 10; i++{ workers[i].in <- 'a' + i } for i := 0; i < 10; i++{ workers[i].in <- 'A'+ i } //Put two <-worker.dones together for _, worker := range workers{ <- worker.done <- worker.done } }
However, it is important to note that:
We've created 10 goroutine s, sent data to all channels in the second for, then the third for, and sent data to the channel, which is deadlocked because no one picks up the data in the first channel, and then sends it to the channel.
Solution: Increase concurrency in the DoWork function, keep done <-true in a concurrent execution state, and send data to the main at any time.
func DoWork( id int, c chan int, done chan bool){ for { n := <-c //Accept channel content fmt.Printf("worker %d received %c\n", id, n) go func() { done <- true }() } }
2. Use the'WaitGroup'provided by the system to wait for the task to finish
WaitGroup provides: Add(), Wait(), Done() methods
package main import ( "fmt" "sync") type Worker struct{ in chan int wg *sync.WaitGroup //Reference requires pointer } func DoWork( id int, c chan int, wg *sync.WaitGroup){ for { n := <-c //Accept channel content fmt.Printf("worker %d received %c\n", id, n) wg.Done() //Receive and receive end information } } func createWorker(id int, wg *sync.WaitGroup) Worker { //Create an object w for Worker w := Worker{make(chan int), wg,} go DoWork(id, w.in, wg) return w } func ChanDemo() { var wg sync.WaitGroup var workers [10]Worker for i := 0; i < 10; i++{ workers[i] = createWorker(i, &wg) //Pointer } wg.Add(20) //20 tasks for i := 0; i < 10; i++{ workers[i].in <- 'a' + i } for i := 0; i < 10; i++{ workers[i].in <- 'A'+ i } wg.Wait() //Task End } func main(){ ChanDemo() }
Print results:
worker 0 received a
worker 4 received e
worker 6 received g
worker 2 received c
worker 9 received j
worker 7 received h
worker 0 received A
worker 8 received i
worker 5 received f
worker 3 received d
worker 1 received b
worker 1 received B
worker 9 received J
worker 2 received C
worker 3 received D
worker 4 received E
worker 7 received H
worker 8 received I
worker 6 received G
worker 5 received F
2. Here we summarize several common problems:
func ChanDemo() { var workers [10]Worker for i := 0; i < 10; i++{ workers[i] = createWorker(i) } for i := 0; i < 10; i++{ workers[i].in <- 'a' + i }
In the first for, step 1 workers[0] = createWorker(0)
And then come in here
func createWorker(id int) Worker { //Create an object w for Worker w := Worker{make(chan int), make(chan bool), } go DoWork(id, w.in, w.done) return w }
In this function we open a goroutine, and we'll return w to workers[0], then we'll enter:
for i := 0; i < 10; i++{ workers[i] = createWorker(i) }
Second, third cycle...
Until the end of the loop.
But there's a problem here. We have 10 goroutine s along the way, but they're all waiting (because we haven't given channel anything yet, as we can see from our output)
- So are the 10 goroutines here waiting because we channel s don't receive any information, so it causes goroutines to wait?
2. And here:
func DoWork( id int, c chan int, done chan bool){ for { n := <-c //Accept channel content fmt.Printf("id: %v, chan:%c\n", id, n) done <- true } }
This dead loop, why only loop once after a function call? Of course I know here that he is one of the goroutine s
3. Of course, there is another problem, that is, on the basis of the first two questions, when we call the function DoWork(), we also send the true to the corresponding workers[i].done, and then:
for i := 0; i < 10; i++{ workers[i].in <- 'a' + i } for _, worker := range workers { <- worker.done }
In the second for here, <-worker.done is all true, do we know from here that the 10 previous goroutine s are over?
4. That's why we don't need a time package to estimate how long the program will run?
3. Answer
Yes, they are all waiting for someone to send task data from in.
This is a dead loop, as we usually say in our goroutine, do it whenever there is a task. The video actually executes two times in uppercase and lowercase letters. How many times it executes depends on the outside world, here is the main function, how many tasks are sent to this worker[i].
Here the true direction classmates made a mistake, is the worker notification main function, said I have finished. <- worker.done Here is the main function to receive worker.done data, if received, it means that this worker is finished.
Yes, ideally you should not need a time package to estimate run time. The expected time will be unreliable.