Каналы обмена данными

Общий вид взаимодействия через канал

канал <- значение // Блокирующая передача
<-канал           // Прием данных и их уничтожение
x := <-канал      // Прием и сохранение данных
x, ok := <-канал  // Как и выше, плюс проверка - открыт ли канал и имеются ли данные

Замечания

Действуют по принципу FIFO

Создание канала

Создать канал можно с помощью функции make и объявляются конструкцией chan Тип:

make(chan Тип) // синхронный канал
make(chan Тип, емкость) // асинхронный (буферизированный) канал
// Пример:
messages := make(chan string, 10)  // 10 - размер буфера

// однонаправленные каналы
chan<- Тип  // описывает канал, позволяющий только посылать значения
<-chan Тип  // описывает канал, позволяющий только принимать значения

Если буфер заполнен, нельзя будет писать в канал, пока не будет извлечен хотя бы один элемент (по умолчанию размер буфера - 0). Канал, в котором буфер имеет нулевой размер, может использоваться для отправки значений, только когда на другом конце канала ожидается прием данных. (Эффекта неблокирующих каналов можно добиться с помощью инструкции select)

// write to channel
messages <- "Leader"
messages <- "Follower"
// read from channel
message1 := <-messages
message2 := <-messages

Обычно каналы создаются для обеспечения обмена данными между go-подпрограммами. Блокирующее поведение каналов можно использовать для синхронизации.

Закрытие канала

Закрыть канал может только sender, не receiver. Отправка данных в закрытый канал вызовет panic. Каналы работают не так, как файлы: их не обязательно закрывать. Закрывайте, если вам необходимо сообщить получателю, что данных больше не будет (например в цикле через range).

Пример:

c := make(chan int, 10)
x := 1
c <- x
close(c)

Пример

func main() {
    questions := make(chan string)
    defer close(questions)
    answers := createSolver(questions)
    defer close(answers)
    interact(questions, answers)
}

func createSolver(questions chan string) chan string {
    answers := make(chan string)
    go func() {
        for {
            someStr := <-questions
            // ...
            answers <- someStr
        }
    }()
    
    return answers
}

select

Оператор select позволяет горутине ожидать выполнения нескольких коммуникационных операций.

select блокируется до тех пор, пока не будет запущен один из его вариантов, а затем он выполнит этот case. Он выбирает один случайным образом, если готовы несколько.

select {
case c <- x:
	x, y = y, x+y
case <-quit:
	fmt.Println("quit")
	return
}

Подробнее про этот statement см в разделе Процедурное программированее / Ветвления.

Last updated