Go 中的并发:Goroutines、Channel 和同步原语如何像复仇者联盟一样协同工作

0 评论
/ /
374 阅读
/
6356 字
21 2023-08

In this article, we’ll explore how concurrency works in Go, using the analogy of the Avengers, the popular superhero team. We’ll see how Go’s main concurrency features — Goroutines, Channels, and Synchronisation Primitives — are like the Avengers, each with their own strengths and roles, but also able to cooperate and coordinate with each other. By the end of this article, you’ll have a better understanding of how concurrency in Go is implemented and managed.

Goroutines
The Independent Superheroes The first concurrency feature we’ll look at is Goroutines. Goroutines are like the individual superheroes of the Avengers, such as Iron Man, Captain America, or Black Widow. They are lightweight and can perform different tasks independently, without blocking each other. Each Goroutine has its own stack, registers, and local variables, but can also access shared variables in memory.


The Scheduler
The Leader of the Avengers But how do Goroutines know when to run and when to pause? That’s where the Scheduler comes in. The Scheduler is like the leader of the Avengers, such as Nick Fury or Captain America. It’s responsible for managing and coordinating the Goroutines. The Scheduler uses a technique called “preemptive multitasking” to give each Goroutine a fair chance to run on a logical processor. A logical processor is a virtual CPU that can execute one Goroutine at a time. The Scheduler can switch between Goroutines on the same logical processor, or move Goroutines from one logical processor to another, depending on their availability and priority.


Channels
The Communication Devices of the Avengers One of the key aspects of the Avengers is their communication. They use various devices, such as earpieces, radios, or holograms, to communicate with each other and coordinate their actions. Similarly, Go provides Channels as a way for Goroutines to communicate and synchronise with each other. Channels are like pipes that can transfer data between Goroutines. A Goroutine can send a value to a Channel using the <- operator, and another Goroutine can receive that value from the Channel using the same operator. Channels can be buffered or unbuffered, depending on whether they can store values or not.


Synchronisation Primitives
The Tools for Coordination of the Avengers When the Avengers face a common enemy, they need to coordinate their actions and avoid conflicts. For example, they need to make sure they don’t shoot each other by mistake, or that they don’t interfere with each other’s plans. To achieve this, they use various tools and techniques, such as signals, codes, or strategies. In Go, we have Synchronization Primitives as tools for coordinating Goroutines. Synchronization Primitives are types or functions that help Goroutines access shared resources safely and efficiently. Some examples of Synchronization Primitives are Mutexes, Condition Variables, Wait Groups, and Atomic Operations.


Inside Go’s Concurrency Implementation
How It All Works Together Now that we’ve seen the main concurrency features in Go, let’s take a look at how they work together internally. The Scheduler uses an algorithm called “work stealing” to balance the load of Goroutines across logical processors. It also uses a mechanism called “preemption” to interrupt long-running Goroutines and give other Goroutines a chance to run. Channels use data structures called “synchronous queues” to store and transfer values between Goroutines. They also use Mutexes and Condition Variables internally to ensure safe and efficient communication. Synchronisation Primitives use low-level instructions called “atomic operations” to manipulate shared variables without locking them.

Conclusion
In this article, we’ve seen how concurrency in Go works using the analogy of the Avengers. We’ve learned how Goroutines are like independent superheroes that can perform different tasks simultaneously; how the Scheduler is like the leader of the Avengers that manages and coordinates the Goroutines; how Channels are like communication devices that allow Goroutines to exchange data and synchronise; and how Synchronisation Primitives are like tools for coordination that help Goroutines access shared resources safely and efficiently.


I hope you enjoyed this article and learned something new about concurrency in Go. If you want to learn more about Go’s concurrency features, you can check out these resources:

https://golang.org/doc/effective_go#concurrency
https://tour.golang.org/concurrency/1
https://blog.golang.org/pipes
https://blog.golang.org/waza-talk
https://blog.golang.org/advanced-go-concurrency-patterns