SoFunction
Updated on 2025-03-05

A brief discussion on advanced practice of Go Channel

channel is a very interesting feature in golang. In my experience using golang encoding, most events are enjoying the fun of channel and goroutine. So this article mainly introduces some interesting uses of channel.

Here is the description of channel (channel) in the Go programming language specification translated by Oling Cat:

Channels provide a mechanism that synchronizes between two concurrently executed functions and communicates by passing values ​​(corresponding to the channel element type).

This description is boring and boring. When I first read it, I didn't understand what this was. In fact, a channel can be considered a pipeline or a first-in-first-out queue, which is very simple and lightweight. Channel is not first created by Golang. It also appears in other languages ​​as a built-in feature. In most cases, it is a feature of a large, dumb, and complex message queue system.

This article mainly talks about practice, and the principle part will be mentioned in a brief introduction. There will be articles on the concurrent implementation of go language and memory model in the future.

The source code implemented by channel is not complicated, so it is recommended to read./golang/go/blob/master/src/runtime/

What is channel for

Meaning: channel is used for communication

In fact: (A copy of the data and passed through the channel, it is essentially a queue)

Where should channel be used

Core: Where communication is needed

For example, the following scenario:

  • Notification Broadcast
  • Exchange data
  • Explicit synchronization
  • Concurrent control
  • ...

remember! Channel is not used to implement lock mechanism. Although it can be used in some places to implement functions similar to read and write locks and protect critical zones, don't use it like this!

Channel use case implementation

Timeout control

//Use to implementfunc main() {
  done := do()
  select {
  case <-done:
    // logic
  case <-(3 * ):
    // timeout
  }
}

func do() <-chan struct{} {
  done := make(chan struct{})
  go func() {
    // do something
    // ...
    done <- struct{}{}
  }()
  return done
}

Get the fastest result

A more common scenario is retry. The first request does not return the result within the specified timeout time. At this time, retry the result for the second time and use the fastest return result of the two times.
The timeout control is above, and the following code part is simply called many times.

func main() {
  ret := make(chan string, 3)
  for i := 0; i < cap(ret); i++ {
    go call(ret)
  }
    (<-ret)
}

func call(ret chan<- string) {
  // do something
  // ...
  ret <- "result"
}

Limit the maximum number of concurrencies

// The maximum number of concurrencies is 2limits := make(chan struct{}, 2)
for i := 0; i &lt; 10; i++ {
  go func() {
    // If the buffer is full, it will block here    limits &lt;- struct{}{}
    do()
    &lt;-limits
  }()
}

for...range priority

for ... range c { do } This writing method is equivalent to if _, ok := <-c; ok { do }

func main() {
  c := make(chan int, 20)
  go func() {
    for i := 0; i &lt; 10; i++ {
      c &lt;- i
    }
    close(c)
  }()
  // When c is closed, the loop will be jumped after taking the elements inside  for x := range c {
    (x)
  }
}

Multiple goroutines synchronous responses

Use close broadcast

func main() {
  c := make(chan struct{})
  for i := 0; i &lt; 5; i++ {
    go do(c)
  }
  close(c)
}

func do(c &lt;-chan struct{}) {
  // Will block until close is received  &lt;-c
  ("hello")
}

Non-blocking select

select itself is blocking. When all branches are not satisfied, it will block all the time. If you want not to block, then a default branch that does nothing is the best choice

select {
case <-done:
  return
default:  
}

for{select{}} Terminate

Try not to use the break label form, but put the conditions that terminate the loop into the for condition to achieve it

for ok {
  select {
  case ch <- 0:
  case <-done:
    ok = false
  }
}

channel features

Basic Features

operate channel with nil Closed channel Normal channel
close panic panic Closed successfully
c<- Block forever panic Blocking or sending successfully
<-c Block forever Never block Blocking or receiving successfully

happens-before feature

  1. Receive happens-before send when unbuffered
  2. In any case, send happens-before Receive
  3. close happens-before Receive

refer to

/article/
/doc/effective_go.html#channels

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.