При работе с проблемой параллелизма сложнее рассуждать о движении вниз по стеку абстракции (машина, процесс, поток, аппаратные компоненты и т. д.). Большинство языков программирования используют поток как высший уровень абстракции. К счастью, Go дополняет это и представляет Горутин.
«Делитесь памятью, общаясь, а не общайтесь, делясь памятью». — один из девизов Go
Хотя Golang предоставляет традиционный механизм блокировки в пакете sync
, его философия предпочитает «делиться памятью путем общения». Поэтому Golang представляет канал в качестве среды для горутин для связи друг с другом.
//goroutine
go func() {
// do some work
}()
//channel
dataStream := make(chan interface{})
Модель форк-соединения
Модель параллелизма fork-join, которой следует Go
Эта модель параллелизма используется в Golang. В любое время; дочерняя горутина может быть разветвлена для выполнения параллельной работы с родительской, а затем присоединяется обратно **в какой-то момент.
В каждой программе Go есть основная горутина. Главный может выйти раньше, чем его дочерние элементы; в результате необходима точка соединения, чтобы убедиться, что дочерняя горутина имеет шанс закончить.
Канал
Канал обладает следующими свойствами:
- Горутина-безопасна (несколько горутин могут получить доступ к общему каналу без условий гонки)
- Семантика очереди FIFO
Канал всегда возвращает 2 значения: 1 — возвращаемый объект, 1 — статус ( true
означает действительный объект, false
означает, что в этом канале больше не будут отправляться значения)
intStream := make(chan int)
close(intStream)
integer, ok := <- intStream
fmt.Printf("(%v): %v", ok, integer) // (false): 0
Направление канала
var dataStream <-chan interface{} //read from only channel
var dataStream chan<- interface{} // write to only channel
var dataStream chan interface{} // 2 ways
Емкость канала
Емкость канала по умолчанию равна 0 (небуферизованный канал). Чтение из пустого или запись в полный канал блокирует.
c := make(chan int,10) //buffered size of 10
c := make(chan int) //unbuffered channel
если буферизованный канал пуст и имеет получателя, буфер будет пропущен и значениебудет передано непосредственно от отправителя к получателю.
Поведение канала против горутины
Когда горутина читает или записывает в канал, в зависимости от состояния канала может происходить различное поведение.
intStream := make(chan int) // 0 capacity channel
go func() {
defer close(intStream)
for i:=1; i<=5; i++{
intStream <- i
}
}()
//range from a channel
for integer := range intStream { // always blocked until the channel is closed
fmt.Printf("%v ", integer)
}
Поскольку поведение сложное, у нас должен быть способ сделать надежную и масштабируемую программу.
Надежный и масштабируемый способ работы с каналом
Вот 1 способ предложения:
- Не более 1 Goroutine владеет каналом. Владение каналом должно быть небольшим, чтобы им можно было управлять.
- Владелец канала имеет права записи (
chan←
); - В то время как потребитель имеет только просмотр только для чтения (
←chan
).
- Владельцы канала
Обязанности:
- Инициальный канал
- Пишите в канал или передайте право собственности другой горутине
- Закрыть канал
- Инкапсулировать и выставить канал как канал читателя
2. Канал потребитель
Обязанности:
- Обработать, когда канал закрыт
- Обработка блокирующего поведения при чтении из канала
chanOwner := func() <-chan int {
resultStream := make(chan int, 5) //init
go func() {
defer close(resultStream) // close
for i:=0;i<=5;i++{
resultStream <- i // write
} }()
return resultStream // read channel returned
}
resultStream := chanOwner()
for result := range resultStream {
fmt.Printf("Received: %d\n", result)
}
fmt.Println("Done receiving!")
Ссылки:
Параллелизм в Go: инструменты и методы для разработчиков — Книга Кэтрин Кокс-Будей
Первоначально опубликовано на https://dev.to 26 мая 2022 г.