1.2Kпросмотров
77.2%от подписчиков
4 марта 2026 г.
Score: 1.3K
😢😢 От этого data race не спасает даже race detector Data race это частный случай race condition. Определять термин data race на самом деле можно по-разному. Самое простое определение:
Состояние во время исполнения, когда две операции над переменной происходят из разных горутин без синхронизации, при этом хотя бы одна из них является записью. Чуть более сложное:
Состояние во время исполнения, когда две конфликтующие операции над переменной происходят из разных горутин без синхронизации. И самое сложное на основании формализма из Go memory model:
Состояние во время исполнения, когда две конфликтующие операции над переменной нельзя разделить отношением happens before Казалось бы, да ну их, есть прекрасный инструмент race detector, который инструментриует программу при компиляции и позволяет во время исполнения находить data race. Но, внезапно, выясняется, что он не всегда может найти проблему. Это напрямую следует из его устройства. Пример: Мейнтейнеры Go пытались реализовать такой интерфейс: type Cron interface { Run(ctx context.Context, action func(), next func() time.Duration)
} Реализация должна вызывать action, ожидая перед каждым запуском next() времени. После отмены контекста не должно быть вызовов action. Такой подход на практике очень часто используется, например: • HTTP ретраи
• Service discovery
• Adaptive polling
• Keepalive / heartbeat
• Любые задачи с backoff Сходу у них получилась следующая реализация: func (c cronImpl) Run(ctx context.Context, action func(), next func() time.Duration) { var t time.Timer t = time.AfterFunc(next(), func() { select { case <-ctx.Done(): return default: action() t.Reset(next()) } }) <-ctx.Done()
} Запускаем тесты: func TestCron(t testing.T) { synctest.Test(t, func(t testing.T) { c := New() var n atomic.Int64 next := func() time.Duration { if n.Add(1) <= 5 { return 0 } return 1 * time.Second } var calls atomic.Int64 ctx, cancel := context.WithCancel(t.Context()) t.Cleanup(func() { cancel() }) done := make(chan struct{}) go func() { defer close(done) c.Run(ctx, func() { calls.Add(1) }, next) }() synctest.Wait() require.Greater(t, calls.Load(), int64(0)) cancel() synctest.Wait() select { case <-done: default: t.Fatalf("Run did not exit after cancel") } })
} Которые успешно проходят даже с race detector'ом. Но в этой реализации действительно есть data race, и race detector его не находит. 👉👉 Пишите ваши предположения в комментариях, пост с правильным ответом выйдет в ближайшее время. Ссылка с примером использования: https://go.dev/play/p/Fq-yNa0wXWj P.S. Пожалуйста, те, кто проходил курс и решал это задание на практике, не пишите сразу правильный ответ)