Как и когда происходит блокировка при использовании небуферизованного канала в Go?

Ответ

Небуферизованный канал (созданный как make(chan T) или make(chan T, 0)) обеспечивает прямую синхронизацию между горутинами. Его также называют каналом для "рандеву" (rendezvous), потому что для успешной передачи данных отправитель и получатель должны встретиться одновременно.

Блокировка происходит всегда и для обеих сторон:


  • При отправке (ch <- value): Отправляющая горутина немедленно блокируется и ждет, пока другая горутина не будет готова принять данные из этого канала.



  • При чтении (<-ch): Читающая горутина немедленно блокируется и ждет, пока другая горутина не отправит данные в этот канал.


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

Классический пример:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string) // Небуферизованный канал

    go func() {
        fmt.Println("Горутина: готовлюсь отправить данные...")
        ch <- "привет" // Блокируется здесь, ждет получателя
        fmt.Println("Горутина: данные успешно отправлены")
    }()

    time.Sleep(2 * time.Second) // Даем время горутине запуститься и заблокироваться
    fmt.Println("Main: готовлюсь получить данные...")
    msg := <-ch // Разблокирует отправителя, и обе горутины продолжат выполнение
    fmt.Printf("Main: получил данные: '%s'n", msg)
}

Этот механизм гарантирует, что сообщение было доставлено и принято, и является мощным инструментом для синхронизации.