Channel in Go ' Runtime

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

One of the big highlights of the go language is support for language-level concurrency. The language level provides concurrent programming, how important it is, you may need to personally understand the multithreading, event +callback and other common concurrency programming model can be accurately felt. To match language-level concurrency support, the channel component is essential for the go language. The official advocate of a programming creed-"use communication to share memory, rather than shared memory to communicate", the "communication to share memory" is the means of channel.

The implementation of the channel is located in the Runtime/chan.c file.

Channel Bottom Structure model

Each channel is defined by a Hchan structure in which two very critical fields are RECVQ and SENDQ. RECVQ and SENDQ are two waiting queues, and the two teams joins respectively hold the goroutine waiting to be read on the channel and Goroutine to wait for a write operation on the channel.

When we use make () to create a channel, the approximate memory model of the channel is like, there is a Hchan structure head, all the memory behind the head is divided into one slot, and each slot will store an element. The number of slots is, of course, the buffer size specified when you make the channel. If make's channel is unbuffered, then there is no slot here, only hchan the head structure. This underlying implementation of the channel is the allocation of contiguous memory (arrays), not the linked list or any other advanced data structure, and in fact does not require advanced data structures to do this thing.

The array of all the slots here itself cannot be FIFO without moving the memory, in fact, there are two key fields in Hchan, RECVX and Sendx, which, with their mates, make the slot array a loop array, thus using the array to implement a circular queue.

There's a little piece of code here that's going to make a channel function.

#defineMAXALIGN7Hcan *c;// calculate rounded size of Hchann = sizeof(*c);while(n & MAXALIGN)n++;

The while loop here is to make the size of the Hchan structure up to a multiple of 8, so that the memory space behind it is aligned by 8 bytes. In order to complete this upward completion, the worst case is to perform 7 cycles, and in fact can be one step to a multiple of 8, there is absolutely no need to add 1 at a time to try. This detail is actually in a lot of code, Nginx is very elegant. I want to say that part of the code of Go is quite unrestrained, I personally do not like the name of some functions/variables inside runtime.

Write Channel

With the underlying structural model of the channel, you can basically imagine how an element is "enqueued/out" on the channel. The function that completes the write channel operation is runtime·chansend that the function implements both synchronous/asynchronous write channel, that is, the write operation of the channel with/without buffering is implemented in this function. Synchronous writing, or asynchronous writing, is actually to determine if there is a slot. The process of writing the channel is described here, and the code is no longer displayed.

    1. Lock the entire channel structure (the map model above). Locking is understandable, but the lock is big enough. Therefore, it is important to consider whether you must always use "communication to share memory". This lock can be seen, the channel is not necessarily directly to the shared variable lock efficiency is high.
    2. Now that the channel has been locked, you can start working. Determine if there is a slot (whether with buffering), if there is an asynchronous write, do not write synchronously.
    3. Assuming that the 2nd step is to write synchronously , then try to get recvq a waiting goroutine from the waiting queue, and then give the element to be written directly to (copy) the Goroutine, Then set the goroutine of the element to the ready state to start running. Here is not the end, if there recvq is no waiting for the goroutine, then the element to be written is saved in the current execution write goroutine structure, and then the current goroutine into the queue sendq and suspended, Waits for someone to read the element before it is awakened. This time, the synchronous writing process is really finished.
    4. Assuming that the 2nd step is asynchronous write , asynchronous write relative synchronous writes, the dependent object is no longer whether there is goroutine waiting to be read, but whether the buffer is full (there is still slot). Therefore, the asynchronous write process and synchronous write are generally the same. The first is to determine if there is a slot available, and if no slot is available, the current goroutine is queued up sendq and suspended for waiting. If a slot is available, append the element to a slot, and try to recvq remove a waiting goroutine from the middle to begin the read operation (if there are goroutine waiting to be read in RECVQ). Here, the asynchronous write is done.

Asynchronous writes and synchronous writes are essentially the same in a logical process, except that they depend on different objects. Synchronous write relies on whether there are goroutine waiting to be read, and whether the asynchronous write depends on the buffer available.

Read Channel

We know the logic of the writing process and try to speculate that the reading process is not difficult at all. With writing, it is essentially read. The function to complete the Read channel operation is runtime·chanrecv to briefly describe the reading process.

    1. Also first lock, lock the whole channel good work.
    2. A It is similar to the write process to determine whether to do synchronous or asynchronous reading with or without buffering.
    3. If you are reading synchronously, try to fetch sendq a goroutine from the queue and take the elements that need to be written (copy), and then set the Goroutine out to ready. If sendq there is no waiting to write the Goroutine, you can only goroutine the current read to the queue recvq and be suspended.
    4. The assumption is asynchronous read, this time is to determine whether there is an element in the buffer, no, is the current read goroutine to the queue recvq and suspended waiting. If there is an element, it is, of course, to take out the first element and try to sendq pull out a goroutine that waits to be written.

Through the reading and writing process can be seen, reading and writing is heart-to-heart, there is a very important detail-read need to "wake up" write Goroutine, write the time need to "wake up" read the goroutine. So the reading and writing process here is actually in pairs, with the completion of work, the lack of a can not. (I seem to be talking nonsense)

Realization of infinite Channel

Some colleagues mentioned how to implement a channel that does not limit buffer size, and also supports select operations. Select implementation, put down a discussion. No matter what language it is, it should be easy to achieve an unrestricted channel. On the basis of the current channel how to achieve an unrestricted size channel, here I would like to say something about my idea, a point.

Now the channel is actually an array, in order to avoid the memory copy, you can add a layer of linked list structure on the current basis. This way, as long as the buffer is exhausted, a new slot array can be allocated, and the old array is chained together to form a larger buffer. The most complex of the code here should be that the element is read away and the empty array needs to be freed. Joining the list to construct an unrestricted channel implementation looks like a relatively simple and effective solution.

If the channel is unrestricted buffer size, then the written goroutine will never be suspended waiting, also do not sendq queue. Of course, without a consumer or consumer hanging out, the channel will eventually cause memory to explode. So, is it really necessary to have a channel of unlimited size???

Understanding the underlying implementation of the channel, it should be better to choose "Communication to share memory, or share memory to communicate", there is no silver bullet.

Note: This article is based on the go1.1.2 version code.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.