Ответ
Блокировка — это фундаментальное свойство каналов в Go, которое служит основным механизмом для синхронизации горутин и управления потоком данных.
Когда канал блокируется?
Блокировка происходит в следующих ситуациях:
Отправка данных (
ch <- data
):- В небуферизованный канал: горутина-отправитель блокируется до тех пор, пока другая горутина не будет готова прочитать данные из этого канала.
- В буферизованный канал: горутина-отправитель блокируется, если буфер канала полон.
Чтение данных (
<-ch
):- Из небуферизованного или пустого буферизованного канала: горутина-получатель блокируется до тех пор, пока другая горутина не отправит данные в этот канал.
Основные цели блокировки
- Синхронизация: Блокировка гарантирует, что одна горутина дождется другой в точке обмена данными. Это позволяет координировать их выполнение без использования примитивов синхронизации, таких как мьютексы.
- Управление потоком (Flow Control): Если горутина-производитель работает быстрее горутины-потребителя, заполненный буфер канала заблокирует производителя, не давая ему "затопить" потребителя данными. Это естественный механизм противодействия.
Пример
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string) // Небуферизованный канал
go func() {
fmt.Println("Горутина: готовлюсь отправить данные...")
ch <- "Привет из горутины!" // Эта горутина заблокируется здесь...
fmt.Println("Горутина: данные успешно отправлены.")
}()
fmt.Println("Main: жду 2 секунды перед чтением...")
time.Sleep(2 * time.Second)
fmt.Println("Main: читаю данные из канала...")
message := <-ch // ...пока main-горутина не дойдет до этой строки и не прочитает значение.
fmt.Printf("Main: получил сообщение: '%s'n", message)
}
Таким образом, блокировка канала — это не побочный эффект, а ключевая особенность, реализующая один из главных принципов Go: "Не обменивайтесь данными через общую память; обменивайтесь памятью через каналы".