Обратите внимание, что это обновленный ответ, поскольку с оригиналом были проблемы.
Как указывалось другими, вы не можете избежать состояния гонки без дополнительной синхронизации. Вы можете использовать Mutex, но sync.Cond
кажется подходящим. В коде ниже принимающая горутина сигнализирует о том, что она получила значение от чана. Он отменяет контекст перед сигнализацией (используя Cond.Signal
), и отправляющая горутина ожидает сигнала. Это позволяет избежать состояния гонки, поскольку статус контекста обновляется до того, как его можно будет проверить.
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan int)
cond := sync.NewCond(&sync.Mutex{}) // *** new
go func() {
defer close(ch)
cond.L.Lock() // *** new
defer cond.L.Unlock() // *** new
for i := 1; ; i++ {
ch <- i // *** moved
cond.Wait() // *** new
if ctx.Err() != nil { // *** changed
return
}
}
}()
print(<-ch)
cond.Signal() // *** new
print(<-ch)
cond.Signal() // *** new
print(<-ch)
cancel()
cond.Signal() // *** new
print(<-ch)
cond.Signal() // *** new
Это самый простой способ увидеть, что принимающая горутина не получит больше значений в канале после того, как она отменил контекст.
Попробуйте на Playground