Я делаю свой первый опыт в го, и до сих пор мне очень нравится конструкция горутин и каналов.Мне интересно, существует ли идиоматический способ избежать тупиков в двунаправленной связи между несколькими программами.Рассмотрим следующий пример.Есть три программы: производитель, рабочий и контроллер .
Производитель производит целые числа.В действительности это могут быть данные, поступающие, например, из сетевого подключения.
Рабочий получает данные от производителя и выполняет с ними определенные операции.Затем работник направляет измененные данные в контроллер.
В некоторых случаях контроллер отправляет команду работнику.В этом примере это может произойти, если полученное целое число больше 180.
Тупик возникает, когда контроллер пытается отправить команду работнику, а работник пытается отправить целое число.к контроллеру.
producerToWorker := make(chan int)
workerToController := make(chan int)
controllerToWorker := make(chan bool) // bool represents a command for this example
// Worker
go func() {
for {
select {
case i := <-producerToWorker:
// Do some processing and send to controller
workerToController <- (2 * i) + 1
case <-controllerToWorker:
// Would react to the command here
}
}
}()
// Controller
go func() {
for {
select {
case i := <-workerToController:
fmt.Println(i)
if i > 180 {
// Send a command to the worker
controllerToWorker <- true
}
}
}
}()
// Producer
for {
producerToWorker <- rand.Intn(100)
}
Пример вывода:
163
175
95
113
1
189 // No deadlock
23
125
179
57
149
23
91
191 // No deadlock
133
95
175
177
181 // No deadlock
17
175
63
27
181 // Deadlock!
fatal error: all goroutines are asleep - deadlock!
Буферизация каналов сделает этот тупик менее вероятным, но не решит его логически.Я хотел бы избежать мьютексов, если это возможно.Как вы справляетесь с такими ситуациями в Go?
Edit : Чтобы дать более реальное описание: я столкнулся с этой проблемой, когда пытался реализовать клиент websocket.Клиент веб-сокета ( работник ) подключается к внешней службе ( продюсер ) и получает от нее сообщения ( providerToWorker ) и передает их контроллеру (workerToController ) для обработки полученных сообщений.Контроллер должен реагировать на полученные сообщения, например отправлять ответ или отключать клиента при получении недопустимого сообщения ( controllerToWorker ).