channel 2 concurrently published by Golang's Way to Growth

Keywords: Go

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)

  1. 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
  1. Yes, they are all waiting for someone to send task data from in.

  2. 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].

  3. 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.

  4. Yes, ideally you should not need a time package to estimate run time. The expected time will be unreliable.

Posted by kristalys on Mon, 04 Oct 2021 09:56:20 -0700