cpp

Каналы

Для обмена данными между потоками предназначены каналы. Объявление канала выполняется с помощью ключевого слова chan по следующей схеме:

var <Название канала> chan <Тип данных>

Пример объявления канала, содержащего данные типа int:

var ch chan int

После такого объявления переменная будет иметь значение nil, поэтому напрямую использовать переменную нельзя, ее нужно дополнительно инициализировать с помощью глобальной функции make():

var ch chan int
fmt.Println(ch)        // <nil>
fmt.Printf("%T\n", ch) // chan int
var ch2 chan int = make(chan int)
fmt.Println(ch2)       // 0xc000018180

Запись в канал осуществляется по следующей схеме:

Название_канала <- Значение

Чтение из канала выполняется по следующей схеме:

Переменная = <- Название_канала

Каналы бывают небуфферизированными и буфферизированными.

Небуфферизированные каналы

Небуфферизированные каналы могут содержать только одно значение. Поток-писатель блокируется до момента освобождения канала. После записи поток-писатель блокируется до момента прочтения другим потоком. Поток-читатель блокируется до момента появления данных в канале. После чтения данные удаляются из канала. Чтение и запись должны выполняться разными потоками, иначе возникнет состояние взаимной блокировки (deadlock). В случае взаимной блокировки возникнет фатальная ошибка и программа аварийно завершится со следующим сообщением об ошибке:

fatal error: all goroutines are asleep - deadlock!

Объявление и инициализация небуфферизированных каналов выполняется по следующим схемам:

var <Название канала> chan <Тип данных> = make(chan <Тип данных>)
<Название канала> := make(chan <Тип данных>)

Пример объявления каналов, содержащих данные типа int:

var ch chan int = make(chan int)
ch2 := make(chan int)

Запись в канал осуществляется по следующей схеме:

Название_канала <- Значение

Если в канале уже есть данные, то поток блокируется до момента освобождения канала. Пример записи в канал числа 10:

ch <- 10

Чтение из канала выполняется по следующей схеме:

Переменная = <- Название_канала

Если в канале нет данных, то поток блокируется до момента появления данных. Пример чтения из канала:

v := <-ch

Создадим два потока и передадим им каналы разными способами (листинг 18.8).

Листинг 18.8. Небуфферизированные каналы

package main

import (
   "fmt"
   "sync"
)

func main() {
   var wg sync.WaitGroup
   var ch chan int = make(chan int)
   ch2 := make(chan int)
   wg.Add(2)
   go func() {
      defer wg.Done()
      v := <-ch // Читаем данные из канала
      fmt.Println(v)
   }()
   go test(ch2, &wg)
   ch <- 10  // Записываем данные в канал
   // Поток блокируется до момента прочтения
   ch2 <- 20 // Записываем данные в канал
   // Поток блокируется до момента прочтения
   wg.Wait()
   fmt.Println("Конец функции main()")
}
func test(ch chan int, wg *sync.WaitGroup) {
   defer wg.Done()
   v := <-ch // Читаем данные из канала
   fmt.Println(v)
}

Итак, внутри анонимной функции выполняется захват контекста, поэтому мы можем работать с каналом ch напрямую. При использовании обычной функции канал ch2 нужно передавать через параметр. Обратите внимание: мы передаем канал по значению, т. к. переменная содержит лишь указатель на данные. Этот указатель копируется при передаче канала в функцию, при этом данные не копируются. А вот экземпляр структуры WaitGroup нужно обязательно передавать по указателю. Копировать его нельзя.

Учебник Go (Golang)
Учебник Go (Golang) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов

cpp