In-depth Golang Channel: Implementation principles and performance optimization suggestions
Golang’s Channel is a key component of its CSP concurrency model and a bridge for communication between Goroutines. Channel is frequently used in Golang, and it is crucial to have a deep understanding of its internal implementation principles. This article will analyze the underlying implementation of Channel based on the Go 1.13 source code.
Basic usage of Channel
Before formally analyzing the implementation of Channel, let’s review its basic usage:
package main import "fmt" func main() { c := make(chan int) go func() { c <- 1 // 发送操作 }() x := <-c // 接收操作 fmt.Println(x) }
This code shows two basic operations of Channel:
- Send operation:
c
- Receive operation:
x :=
Channel is divided into buffered Channel and non-buffered Channel. The above code uses a non-buffered Channel. In a non-buffered Channel, if no other Goroutine is currently receiving data, the sender will block at the send statement.
You can specify the buffer size when initializing the Channel. For example, make(chan int, 2)
specifies the buffer size to be 2. Before the buffer is full, the sender can send data without blocking without waiting for the receiver to be ready. But if the buffer is full, the sender will still block.
Channel underlying implementation function
Before diving into the Channel source code, you need to find the specific implementation location of Channel in Golang. When using Channel, the underlying functions such as runtime.makechan
, runtime.chansend
and runtime.chanrecv
are actually called.
You can use the go tool compile -N -l -S hello.go
command to convert the code into assembly instructions, or use the online tool Compiler Explorer (for example: go.godbolt.org/z/3xw5Cj). By analyzing the assembly instructions, we can find:
-
make(chan int)
corresponds to theruntime.makechan
function. c corresponds to the <code>runtime.chansend
function.x := corresponds to the <code>runtime.chanrecv
function.
The implementation of these functions are located in the runtime/chan.go
file of the Go source code.
Channel structure
make(chan int)
will be converted into a runtime.makechan
function by the compiler, and its function signature is as follows:
func makechan(t *chantype, size int) *hchan
Among them, t *chantype
is the Channel element type, size int
is the user-specified buffer size (0 if not specified), and the return value is *hchan
. hchan
is the internal implementation structure of Channel in Golang, defined as follows:
type hchan struct { qcount uint // 缓冲区中已放入元素的数量 dataqsiz uint // 用户构造Channel时指定的缓冲区大小 buf unsafe.Pointer // 缓冲区 elemsize uint16 // 缓冲区中每个元素的大小 closed uint32 // Channel是否关闭,==0表示未关闭 elemtype *_type // Channel元素的类型信息 sendx uint // 缓冲区中发送元素的索引位置(发送索引) recvx uint // 缓冲区中接收元素的索引位置(接收索引) recvq waitq // 等待接收的Goroutine列表 sendq waitq // 等待发送的Goroutine列表 lock mutex }
The attributes in hchan
are roughly divided into three categories:
-
Buffer related attributes: such as
buf
,dataqsiz
,qcount
, etc. When the buffer size of the Channel is not 0, the buffer is used to store the data to be received, and is implemented using a ring buffer. -
Waiting queue related attributes:
recvq
contains Goroutine waiting to receive data,sendq
contains Goroutine waiting to send data.waitq
Implemented using a doubly linked list. -
Other attributes: such as
lock
,elemtype
,closed
, etc.
makechan
function mainly performs some legality checks and memory allocation of attributes such as buffers and hchan
, which will not be discussed in depth here.
Based on a simple analysis of the hchan
attribute, it can be seen that there are two important components: buffer and waiting queue. All behaviors and implementations of hchan
revolve around these two components.
Channel data sending
The sending and receiving processes of Channel are very similar. First analyze the sending process of Channel (for example c ).
tries to send data to the Channel, if the recvq
queue is not empty, a Goroutine waiting to receive data will be taken out from the recvq
header and the data will be sent directly to the Goroutine. The code is as follows:
package main import "fmt" func main() { c := make(chan int) go func() { c <- 1 // 发送操作 }() x := <-c // 接收操作 fmt.Println(x) }
recvq
Contains Goroutine waiting to receive data. When a Goroutine uses a receive operation (such as x := ), if <code>sendq
is not empty at this time, a Goroutine will be taken from sendq
and the data will be sent to it.
If recvq
is empty, it means that there is no Goroutine waiting to receive data at this time, and the Channel will try to put the data into the buffer:
func makechan(t *chantype, size int) *hchan
The function of this code is very simple, it is to put data into the buffer. This process involves the operation of a ring buffer, dataqsiz
represents the user-specified buffer size (defaults to 0 if not specified).
If a non-buffered Channel is used or the buffer is full (c.qcount == c.dataqsiz
), the data to be sent and the current Goroutine will be packaged into a sudog
object, placed in sendq
, and the current Goroutine will be set to wait Status:
type hchan struct { qcount uint // 缓冲区中已放入元素的数量 dataqsiz uint // 用户构造Channel时指定的缓冲区大小 buf unsafe.Pointer // 缓冲区 elemsize uint16 // 缓冲区中每个元素的大小 closed uint32 // Channel是否关闭,==0表示未关闭 elemtype *_type // Channel元素的类型信息 sendx uint // 缓冲区中发送元素的索引位置(发送索引) recvx uint // 缓冲区中接收元素的索引位置(接收索引) recvq waitq // 等待接收的Goroutine列表 sendq waitq // 等待发送的Goroutine列表 lock mutex }
goparkunlock
will unlock the input mutex and suspend the current Goroutine, setting it to a wait state. gopark
and goready
appear in pairs and are reciprocal operations.
From the user's perspective, after calling gopark
, the code statement for sending data will block.
Channel data reception
The receiving process of Channel is basically similar to the sending process, so I won’t go into details here. The buffer-related operations involved in the reception process will be described in detail later.
It should be noted that the entire sending and receiving process of Channel is locked using runtime.mutex
. runtime.mutex
is a lightweight lock commonly used in runtime-related source code. The whole process is not the most efficient lock-free solution. There is an issue about lock-free Channel in Golang: go/issues#8899.
Channel ring buffer implementation
Channel uses a ring buffer to cache written data. Ring buffers have many advantages and are ideal for implementing fixed-length FIFO queues.
The implementation of the ring buffer in Channel is as follows:
There are two buffer-related variables inhchan
: recvx
and sendx
. sendx
represents a writable index in the buffer, and recvx
represents a readable index in the buffer. Elements between recvx
and sendx
represent data that has been put into the buffer normally.

You can directly use buf[recvx]
to read the first element of the queue, and use buf[sendx] = x
to put the element at the end of the queue.
Buffer writing
When the buffer is not full, the operation of putting data into the buffer is as follows:
package main import "fmt" func main() { c := make(chan int) go func() { c <- 1 // 发送操作 }() x := <-c // 接收操作 fmt.Println(x) }
chanbuf(c, c.sendx)
is equivalent to c.buf[c.sendx]
. The above process is very simple, just copy the data to the buffer location sendx
.
Then, move sendx
to the next position. If sendx
reaches the last position, it is set to 0, which is a typical end-to-end approach.
Buffer reading
When the buffer is not full, sendq
must also be empty (because if the buffer is not full, the Goroutine sending the data will not be queued, but will directly put the data into the buffer). At this time, the reading logic of Channel chanrecv
is relatively simple. Data can be read directly from the buffer. It is also a process of moving recvx
, which is basically the same as the buffer writing above.
When there is a waiting Goroutine in sendq
, the buffer must be full at this time. At this time, the reading logic of Channel is as follows:
func makechan(t *chantype, size int) *hchan
ep
is the address corresponding to the variable that receives the data (for example, in x := , <code>ep
is the address of x
). sg
represents the first sendq
taken from sudog
. In the code:
-
typedmemmove(c.elemtype, ep, qp)
means copying the currently readable element in the buffer to the address of the receiving variable. -
typedmemmove(c.elemtype, qp, sg.elem)
means copying the data waiting to be sent by Goroutine insendq
to the buffer. Becauserecv
is executed later, it is equivalent to placing the data insendq
at the end of the queue.
Simply put, here Channel copies the first data in the buffer to the corresponding receiving variable, and at the same time copies the elements in sendq
to the end of the queue, thereby implementing FIFO (first in, first out).
Summary
Channel is one of the most commonly used facilities in Golang. Understanding its source code will help you better use and understand Channel. At the same time, do not be overly superstitious and rely on the performance of Channel. The current design of Channel still has a lot of room for optimization.
Optimization suggestions:
- Use a more lightweight locking mechanism or lock-free scheme to improve performance.
- Optimize buffer management and reduce memory allocation and copy operations.
Leapcell: The best serverless platform for Golang web applications

Finally, I recommend a platform that is very suitable for deploying Go services: Leapcell
- Multi-language support: Supports JavaScript, Python, Go or Rust development.
- Deploy unlimited projects for free: Pay only for what you use, no requests, no fees.
- Extremely cost-effective: Pay as you go, no idle fees. For example: $25 supports 6.94 million requests with an average response time of 60 milliseconds.
- Smooth developer experience: Intuitive UI for easy setup; fully automated CI/CD pipeline and GitOps integration; real-time metrics and logs for actionable insights.
- Easy scalability and high performance: Automatically scale to easily handle high concurrency; zero operational overhead, focus on building.

Please check the documentation for more information!
Leapcell Twitter: https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd
The above is the detailed content of Go Channel Unlocked: How They Work. For more information, please follow other related articles on the PHP Chinese website!

The core features of Go include garbage collection, static linking and concurrency support. 1. The concurrency model of Go language realizes efficient concurrent programming through goroutine and channel. 2. Interfaces and polymorphisms are implemented through interface methods, so that different types can be processed in a unified manner. 3. The basic usage demonstrates the efficiency of function definition and call. 4. In advanced usage, slices provide powerful functions of dynamic resizing. 5. Common errors such as race conditions can be detected and resolved through getest-race. 6. Performance optimization Reuse objects through sync.Pool to reduce garbage collection pressure.

Go language performs well in building efficient and scalable systems. Its advantages include: 1. High performance: compiled into machine code, fast running speed; 2. Concurrent programming: simplify multitasking through goroutines and channels; 3. Simplicity: concise syntax, reducing learning and maintenance costs; 4. Cross-platform: supports cross-platform compilation, easy deployment.

Confused about the sorting of SQL query results. In the process of learning SQL, you often encounter some confusing problems. Recently, the author is reading "MICK-SQL Basics"...

The relationship between technology stack convergence and technology selection In software development, the selection and management of technology stacks are a very critical issue. Recently, some readers have proposed...

Golang ...

How to compare and handle three structures in Go language. In Go programming, it is sometimes necessary to compare the differences between two structures and apply these differences to the...

How to view globally installed packages in Go? In the process of developing with Go language, go often uses...

What should I do if the custom structure labels in GoLand are not displayed? When using GoLand for Go language development, many developers will encounter custom structure tags...


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Dreamweaver CS6
Visual web development tools

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment

VSCode Windows 64-bit Download
A free and powerful IDE editor launched by Microsoft

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

Zend Studio 13.0.1
Powerful PHP integrated development environment