Проиллюстрировать основной принцип канала Голанга.

Go

Без лишних слов, давайте к делу.

Общая структура канала

Краткое описание:

  • bufБуферизованная структура канала, используемая для хранения буферизованных данных. круговой связанный список
  • sendxиrecvxДля записиbufЭта цепочка кругов отправлена ​​или получена ~ ИНДЕКС
  • lockявляется мьютексом.
  • recvqиsendqОчереди структур (sudog), абстрагированные горутинами, которые получают (

Исходный код находится на/runtime/chan.goСредний (текущая версия: 1.11). Структураhchan.

type hchan struct {
    qcount   uint           // total data in the queue
    dataqsiz uint           // size of the circular queue
    buf      unsafe.Pointer // points to an array of dataqsiz elements
    elemsize uint16
    closed   uint32
    elemtype *_type // element type
    sendx    uint   // send index
    recvx    uint   // receive index
    recvq    waitq  // list of recv waiters
    sendq    waitq  // list of send waiters

    // lock protects all fields in hchan, as well as several
    // fields in sudogs blocked on this channel.
    //
    // Do not change another G's status while holding this lock
    // (in particular, do not ready a G), as this can deadlock
    // with stack shrinking.
    lock mutex
}

Ниже мы подробно познакомимhchanКак используется каждая часть.

Начните с создания

Сначала мы создаем канал.

ch := make(chan int, 3)

Создание CHANNEL фактически создается в памяти.hchanструктура и возвращает указатель ch. Мы используем этот указатель для передачи канала между функциями во время использования. Вот почему передача функции не должна использовать указатель канала, а просто использует канал напрямую, потому что сам канал является указателем.

Отправить send(ch

先考虑一个问题,如果你想让goroutine以先进先出(FIFO)的方式进入一个结构体中,你会怎么操作? Замок! правильный! channel就是用了一个锁。 hchan本身包含一个互斥锁mutex

Как устроена очередь в реализации Channel?

В канале есть буфер buf, который используется для буферизации очереди данных (если инстанцируется буферизованный канал). Давайте сначала посмотрим, как реализована «очередь». Или канал, который вы только что создали

ch := make(chan int, 3)

когда используешьsend (ch <- xx)илиrecv ( <-ch), сначала заблокируйтеhchanэта структура.

затем начнитеsend (ch <- xx)данные. один

ch <- 1

два

ch <- 1

три

ch <- 1

На данный момент он заполнен, очередь не может быть заполнена Динамический граф представлен в виде:

затем взятьrecv ( <-ch)Процесс является обратной операцией и также требует блокировки.

затем начнитеrecv (<-ch)данные. один

<-ch

два

<-ch

три

<-ch

На фото:

Обратите внимание на два снимкаbufиrecvxа такжеsendxПеремена,recvxиsendxоснован на круговом связанном спискеbufизменения в связи с изменениями. Что касается того, почему канал использует кольцевой связанный список в качестве структуры кеша, я лично думаю, что список кеша является динамическим.sendиrecvпроцесс, найдите текущийsendилиrecvxрасположение, выборsendсуммаrecvxРасположение более удобное, пока вы продолжаете чередовать операции в порядке связанного списка.

Кэш хранится в порядке связанного списка, а данные считываются в порядке связанного списка при выборке данных, что соответствует принципу FIFO.

Уточнение отправки/получения

Примечание. Каждый шаг кэшированной таблицы цепочек необходим для блокировки!

Детали работы каждого шага могут быть уточнены следующим образом:

  • Во-первых, заблокировать
  • Во-вторых, скопируйте данные из горутины в «очередь» (или из очереди в горутину).
  • В-третьих, снимите блокировку

Работа каждого шага резюмируется в виде динамической диаграммы: (процесс отправки)

Или: (процесс получения)

Так что нетрудно заметить, что классическое предложение в Go:Do not communicate by sharing memory; instead, share memory by communicating.Конкретная реализация заключается в использовании канала для копирования данных с одного конца на другой! действительно подходитchannelАнглийское значение:

Что происходит, когда буфер канала заполнен? В чем причина этого?

При его использовании мы все знаем, что когда буфер канала заполнен или буфера нет, мы продолжаем отправлять (ch

Мы знаем, что горутины Go — это потоки пользовательского режима (user-space threads), потоки пользовательского режима должны быть запланированы сами по себе, и в Go есть планировщик времени выполнения, который помогает нам выполнить планирование. Я не буду здесь вдаваться в подробности о GMP-модели планирования Go, если вы не понимаете, то можете прочитать другую мою статью (Принцип планирования Go)

Блокирующая операция горутины на самом деле вызываетsend (ch <- xx)илиrecv ( <-ch)Когда инициатива вызвала подробности, пожалуйста, прочтите следующее:

//goroutine1 中,记做G1

ch := make(chan int, 3)

ch <- 1
ch <- 1
ch <- 1

На этот раз G1 работает нормально, когда операция отправки (ch

В то же время G1 также будет абстрагироваться в указатель G1 и элемент отправки.sudogСтруктура сохраняется в Гчанеsendqждет, когда его разбудят.

Итак, когда просыпается G1? В это время G2 совершил грандиозный дебют.

G2 выполнил операцию RECVp := <-ch, поэтому произойдут следующие операции:

G2 берет данные из очереди кеша, канал помещает G1 в очередь ожидания, помещает данные, отправленные G1, в кеш, затем вызывает планировщик Go, пробуждает G1 и помещает G1 в готовую к выполнению очередь Goroutine.

Что, если G2 сначала выполняет операцию recv?

Возможно, вы сможете изменить вышеуказанную линию мышления. Во-первых:

В это время G2 будет активно вызывать планировщик Go, пусть G2 подождет, а M будет использоваться другими G. G2 также будет абстрагироваться в указатель G2 и получать пустые элементы.sudogСтруктура сохраняется в hchanrecvqжду когда проснутся

В этот момент появляется горутина G1, которая начинает передавать данные в канал.ch <- 1. 此时,非常有意思的事情发生了:

G1 не блокирует канал, а затем помещает данные в кеш, а напрямую копирует данные из G1 в стек G2. Этот метод очень хорош! В процессе пробуждения G2 больше не нужно запрашивать блокировку канала, а затем извлекать данные из кэша. Уменьшение копирования памяти и повышение эффективности.

Последующие вещи очевидны:

Для получения более интересного контента, пожалуйста, обратите внимание на мой публичный аккаунт WeChat.互联网技术窝Или добавьте WeChat для обсуждения и общения:

использованная литература: