Channels are a feature unique to the Go language and are more abstract and difficult to understand than Goroutine. After all, the latter can be compared to threads and processes. Go Channels Are Bad and You Should Feel Bad mentions the confusion of using Channel and Mutex. I mentioned a simple program that can save the highest score of each player in a game. The authors use channel and Mutex respectively to achieve this function.

The channel version

First, define the Game structure:

type Game struct {
  bestScore int
  scores    chan int
}
Copy the code

Instead of using mutex protection, bestScore uses a separate Goroutine to receive data from a channel and then update its status.

func (g *Game) run() {
  for score := range g.scores {
    if g.bestScore < score {
      g.bestScore = score
    }
  }
}
Copy the code

The constructor is then defined to start a game

func NewGame() (g *Game) {
  g = &Game{
    bestScore: 0,
    scores:    make(chan int),
  }
  go g.run()
  return g
}
Copy the code

Next, the Player interface is defined to return the Player’s score and error to indicate that the Player has abandoned the match.

type Player interface {
  NextScore() (score int, err error)
}
Copy the code

The game receives all players’ scores through channels

func (g *Game) HandlePlayer(p Player) error { for { score, err := p.NextScore() if err ! = nil { return err } g.scores <- score } }Copy the code

In the end, Game was able to achieve thread-safe recording of a player’s highest score, and everything worked out perfectly.

The implementation was so successful that the game service created many games at once. After a while, you find that players occasionally stop playing, and many games no longer have players playing, but there is no mechanism to stop the loop. You are being crushed by the abandoned (*Game).run goroutine.

The mutex version

However, note the simplicity of the solution using Mutex, which does not even have these problems:

type Game struct { mtx sync.Mutex bestScore int } func NewGame() *Game { return &Game{} } func (g *Game) HandlePlayer(p Player) error { for { score, err := p.NextScore() if err ! = nil { return err } g.mtx.Lock() if g.bestScore < score { g.bestScore = score } g.mtx.Unlock() } }Copy the code

Channels are used for orchestration and mutex is used for serialization

If you were implementing it, would you rather use channel or Mutex? On the information provided so far, there is no doubt that I would choose the latter.

What’s the difference between Channel and Mutex? When should channel be used?

Actually Rob Pike sums it up in Go Proverbs:

Channels orchestrate; mutexes serialize.

Translation is a

Channels are used for orchestration and mutex is used for serialization

This sentence is simple, but also abstract. How to understand it?

channel vs mutex

Concurrency is Not Parallelism Rob Pike opened his post about Concurrency is Not Parallelism by saying:

  1. Everything in the world is parallel, but today’s programming languages are object-oriented
  2. Golang hopes to passgoroutine(Concurrent execution),channel(synchronization and data transfer),select(Multi-channel concurrent control)

In a previous post, I mentioned this

For speakers of other languages, flow control in a program generally means:

  • if/else
  • for loop

In Go, a similar understanding is only half right. Because channel and SELECT are the focus of process control. Channels provide powerful capabilities to help flow data from one Goroutine to another. It also means that channel influences both the data flow and the control flow of the program.

A channel is part of the Go language’s parallel tool set. It is responsible for both data flow and control flow. It is the organizer of the program structure. Mutex, by contrast, focuses only on data and guarantees serial access to data

choreography

To talk more about channel layouts, take a look at the Go Concurrency Patterns search example:

/*
Example: Google Search 3.0
Given a query, return a page of search results (and some ads).
Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results.
*/
c := make(chan Result)
go func() { c <- First(query, Web1, Web2) } ()
go func() { c <- First(query, Image1, Image2) } ()
go func() { c <- First(query, Video1, Video2) } ()
timeout := time.After(80 * time.Millisecond)
for i := 0; i < 3; i++ {
    select {
    case result := <-c:
        results = append(results, result)
    case <-timeout:
        fmt.Println("timed out")
        return
    }
}
Copy the code

The parallel structure of the program remains unchanged regardless of whether the program is executed on several core machines, as follows:

When it comes to programming, it can be compared with Kubernetes for service orchestration. If a Goroutine is a container for K8S, a channel is a network of K8S (e.g., overlay). Kubernetes enables users to deploy and scale their microservices applications on any scale, and Golang enables applications to execute and scale fully in parallel on any number of cpus.

conclusion

As Concurrency is not Parallelism illustrates, channels are heavily misused and abused these days. Understand the nature of channel so that you can use the right tools to do the right thing.

Goroutines and channels are big ideas. They’re tools for program construction.

But sometimes all you need is a reference counter.

Go has “sync” and “sync/atomic” packages that provide mutexes, condition variables, etc. They provide tools for smaller problems.

Often, these things will work together to solve a bigger problem.

Always use the right tool for the job.

Go-test: Go-channel-VS-Mutex

Author: Cyningsun Author: www.cyningsun.com/05-15-2021/… Copyright notice: All articles on this blog are licensed under CC BY-NC-ND 3.0CN unless otherwise stated. Reprint please indicate the source!