Channel practical application, this is enough!

Time:2021-9-26

It is said that this article has a bit of a title, but it is definitely dry goods.

There have been many articles about channel, why should I write it? Any knowledge point, as long as you want, you can cut in from different angles! Then write something about the channel application. Deepen your understanding by using the channel feature in different scenarios! So before reading this article, you must first understand channel.

Homicide caused by channel

The above article missed a key knowledge point I think, and we often make mistakes on it. Even those awesome open source projects have had similar bugs.

My question is: which operations of channel will cause panic?

1. Closing a nil value channel will cause panic.
package main

func main() {
  var ch chan struct{}
  close(ch)
}

Channel practical application, this is enough!

2. Closing a closed channel will cause panic.

package main

func main() {
  ch := make(chan struct{})
  close(ch)
  close(ch)
}

Channel practical application, this is enough!

3. Send data to a closed channel.

package main

func main() {
  ch := make(chan struct{})
  close(ch)
  ch <- struct{}{}
}

Channel practical application, this is enough!

The above three channel operations will cause panic.

You might say, how could I make such a stupid mistake. This is just a simple example. The actual project is very complex. If you are not careful, you will forget which g you closed the channel in.

If you don’t feel secure about a piece of code, trust me, even if it doesn’t happen at noon, it will happen sooner or later.

Some applications of channel

  • Signal notification
  • Timeout control
  • Production and consumption model
  • Data transmission
  • Control concurrency
  • mutex
  • one million……

1. Signal notification

There are often scenarios where the downstream is notified to start calculating data when the information collection is completed.

package main

import (
  "fmt"
  "time"
)

func main() {
  isOver := make(chan struct{})
  go func() {
    collectMsg(isOver)
  }()
  <-isOver
  calculateMsg()
}

//Collect
func collectMsg(isOver chan struct{}) {
  time.Sleep(500 * time.Millisecond)
  Fmt.println ("finish acquisition tool")
  isOver <- struct{}{}
}

//Calculate
func calculateMsg() {
  Fmt.println ("start data analysis")
}

If you simply use notification operations, the type is usedstruct{}。 Because empty structures do not occupy memory space in go, I don’t believe you see.

package main

import (
"fmt"
"unsafe"
)

func main() {
  res := struct{}{}
  FMT. Println ("occupied space:", unsafe. Sizeof (RES))
}
//Occupied space: 0

2. Task execution timeout

When we do task processing, we can’t guarantee the processing time of the task. We usually add some timeout controls to deal with exceptions.

package main

import (
  "fmt"
  "time"
)

func main() {
  select {
  case <-doWork():
    Fmt.println ("end of task")
  case <-time.After(1 * time.Second):
    Fmt.println ("task processing timeout")
  }
}

func doWork() <-chan struct{} {
  ch := make(chan struct{})
  go func() {
    //Task processing time
    time.Sleep(2 * time.Second)
  }()
  return ch
}

3. Production and consumption model

Producers only need to pay attention to production without paying attention to consumers’ consumption behavior, let alone whether consumers have completed the implementation. Consumers only care about consumption tasks, not how to produce.

package main

import (
  "fmt"
  "time"
)

func main() {
  ch := make(chan int, 10)
  go consumer(ch)
  go producer(ch)
  time.Sleep(3 * time.Second)
}

//A producer
func producer(ch chan int) {
  for i := 0; i < 10; i++ {
    ch <- i
  }
  close(ch)
}

//Consumer
func consumer(task <-chan int) {
  for i := 0; i < 5; i++ {
    //5 consumers
    go func(id int) {
      for {
        item, ok := <-task
        //If equal to false, the channel is closed
        if !ok {
          return
        }
        FMT. Printf ("consumer:% D, consumed:% d \ n", ID, item)
        //Give others a chance and you won't lose
        time.Sleep(50 * time.Millisecond)
      }
    }(i)
  }
}

4. Data transmission

Geek’s last interesting question, suppose there are fourgoroutine, numbered 1, 2, 3, 4. There will be one every secondgoroutinePrint out its own number. Now let’s write a program that requires the output number to always be printed in the order of 1, 2, 3 and 4. Similar to the figure below,

Channel practical application, this is enough!

package main

import (
  "fmt"
  "time"
)

type token struct{}

func main() {
  num := 4
  var chs []chan token
  //4 work
  for i := 0; i < num; i++ {
    chs = append(chs, make(chan token))
  }
  for j := 0; j < num; j++ {
    go worker(j, chs[j], chs[(j+1)%num])
  }
  //Give the token to the first one first
  chs[0] <- struct{}{}
  select {}
}

func worker(id int, ch chan token, next chan token) {
  for {
    //Get token corresponding to work
    token := <-ch
    fmt.Println(id + 1)
    time.Sleep(1 * time.Second)
    //Pass to next
    next <- token
  }
}

5. Control concurrency

I often write scripts to pull data internally or externally in the early morning, but if concurrent requests are not controlled, it will often lead togroutineFlooding, and then full of CPU resources. What is often out of control means that bad things will happen. For us, we canchannelTo control concurrency.

package main

import (
  "fmt"
  "time"
)

func main() {
  limit := make(chan struct{}, 10)
  jobCount := 100
  for i := 0; i < jobCount; i++ {
    go func(index int) {
      limit <- struct{}{}
      job(index)
      <-limit
    }(i)
  }
  time.Sleep(20 * time.Second)
}

func job(index int) {
  //Time consuming task
  time.Sleep(1 * time.Second)
  FMT. Printf ("task:% d completed \ n", index)
}

Yes, of course,sync.waitGroupit’s fine too.

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    jobCount := 100
    limit := 0
    for i := 0; i < jobCount; i++ {
        limit++
        wg.Add(1)
        go func(item int) {
            defer wg.Done()
            job2(item)
        }(i)

        if limit == 10 {
            wg.Wait()
            limit = 0
        }
    }
}

func job2(index int) {
    //Time consuming task
    time.Sleep(1*time.Second)
    FMT. Printf ("task:% d completed \ n", index)
}

6. Mutex

We can also passchannelImplement a small mutex. By setting a channel with a buffer of 1, if data is successfully sent to the channel, it means that the lock is obtained. Otherwise, the lock is taken by others and wait for others to unlock it.

package main

import (
  "fmt"
  "time"
)

type ticket struct{}

type Mutex struct {
  ch chan ticket
}

//Create a channel with a buffer of 1
func newMutex() *Mutex {
  return &Mutex{ch: make(chan ticket, 1)}
}

//Whoever can put data into the channel with buffer 1 obtains the lock
func (m *Mutex) Lock() {
  m.ch <- struct{}{}
}

//Unlock and take out the data
func (m *Mutex) unLock() {
  select {
  case <-m.ch:
  default:
    Panic ("unlocked")
  }
}

func main() {
  mutex := newMutex()
  go func() {
    //If 1 gets the lock first, then 2 has to wait 1 second to get the lock
    mutex.Lock()
    FMT. Println ("task 1 got the lock")
    time.Sleep(1 * time.Second)
    mutex.unLock()
  }()
  go func() {
    mutex.Lock()
    //If 2 gets the lock first, then 1 has to wait 2 seconds to get the lock
    FMT. Println ("task 2 got the lock")
    time.Sleep(2 * time.Second)
    mutex.unLock()
  }()
  time.Sleep(500 * time.Millisecond)
  //It took a little trick to get the lock here at last
  mutex.Lock()
  mutex.unLock()
  close(mutex.ch)
}

Here, the article is over. Of course, I just listed somechannelApplication scenarios. You can fully use your imagination to build a more perfect and close to production design in practical work.

If you have other different application mode scenarios, please leave a message below to communicate with me.

In addition, I put the source codegithubYes, address:github.com/wuqinqiang/Go_Concurren…

If the article is helpful to you, like, forward and leave messages are a kind of support!
Channel practical application, this is enough!

This work adoptsCC agreement, reprint must indicate the author and the link to this article

Wu qinkuri

Recommended Today

Seven Python code review tools recommended

althoughPythonLanguage is one of the most flexible development languages at present, but developers often abuse its flexibility and even violate relevant standards. So PythoncodeThe following common quality problems often occur: Some unused modules have been imported Function is missing arguments in various calls The appropriate format indentation is missing Missing appropriate spaces before and after […]