HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Теги: go, golang, программирование на go, race-детектор, кэш</p>
1 <p>Теги: go, golang, программирование на go, race-детектор, кэш</p>
2 <p>Golang в своём арсенале имеет такую вещь, как<strong>race detector</strong>.</p>
2 <p>Golang в своём арсенале имеет такую вещь, как<strong>race detector</strong>.</p>
3 <p>Посмотрим на следующий код:</p>
3 <p>Посмотрим на следующий код:</p>
4 type Cache struct { data map[string]int } func NewCache() *Cache { return &amp;Cache{ data: make(map[string]int), } } func (c *Cache) Set(k string, v int) { c.data[k] = v } func (c *Cache) Get(k string) int { if v, ok := c.data[k]; ok { return v } return 0 }<p>Это возможная реализация простейшего кэша, у которого ключ - это строка, а значение - целое число.</p>
4 type Cache struct { data map[string]int } func NewCache() *Cache { return &amp;Cache{ data: make(map[string]int), } } func (c *Cache) Set(k string, v int) { c.data[k] = v } func (c *Cache) Get(k string) int { if v, ok := c.data[k]; ok { return v } return 0 }<p>Это возможная реализация простейшего кэша, у которого ключ - это строка, а значение - целое число.</p>
5 <p>Всё будет хорошо до тех пор, пока кэш используется неконкурентно. Как только речь заходит о чём-то асинхронном (нужно заметить, что язык go отлично для этого подходит), - тут могут появиться неожиданные сюрпризы.</p>
5 <p>Всё будет хорошо до тех пор, пока кэш используется неконкурентно. Как только речь заходит о чём-то асинхронном (нужно заметить, что язык go отлично для этого подходит), - тут могут появиться неожиданные сюрпризы.</p>
6 <p>Взглянем на использование нашего кэша:</p>
6 <p>Взглянем на использование нашего кэша:</p>
7 c := NewCache() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for i := 0; i &lt; 1000; i++ { c.Set("nick", 25) } }() wg.Add(1) go func() { defer wg.Done() for i := 0; i &lt; 1000; i++ { c.Set("nick", 30) } }() wg.Wait() fmt.Println(c.Get("nick"))<p>Код вполне валидный с точки зрения языка, он компилируется, 2 корутины обновляют значение по ключу nick.</p>
7 c := NewCache() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for i := 0; i &lt; 1000; i++ { c.Set("nick", 25) } }() wg.Add(1) go func() { defer wg.Done() for i := 0; i &lt; 1000; i++ { c.Set("nick", 30) } }() wg.Wait() fmt.Println(c.Get("nick"))<p>Код вполне валидный с точки зрения языка, он компилируется, 2 корутины обновляют значение по ключу nick.</p>
8 <h2>Что выведет программа?</h2>
8 <h2>Что выведет программа?</h2>
9 <p>Вариантов несколько: 1) иногда мы можем увидеть 25; 2) иногда мы можем увидеть 30; 3) иногда наше приложение может упасть с ошибкой fatal error: concurrent map writes.</p>
9 <p>Вариантов несколько: 1) иногда мы можем увидеть 25; 2) иногда мы можем увидеть 30; 3) иногда наше приложение может упасть с ошибкой fatal error: concurrent map writes.</p>
10 <p>Для того, чтобы найти проблему в нашем примере, достаточно воспользоваться ключем -race при сборке.</p>
10 <p>Для того, чтобы найти проблему в нашем примере, достаточно воспользоваться ключем -race при сборке.</p>
11 <p>Тогда при запуске мы увидим предупреждение следующего вида:</p>
11 <p>Тогда при запуске мы увидим предупреждение следующего вида:</p>
12 ================== WARNING: DATA RACE Write at 0x00c00009c150 by goroutine 8: runtime.mapassign_faststr() /usr/lib/go-1.13/src/runtime/map_faststr.go:202 +0x0 main.main.func2() /home/yury/race.go:19 +0xb7 Previous write at 0x00c00009c150 by goroutine 7: runtime.mapassign_faststr() /usr/lib/go-1.13/src/runtime/map_faststr.go:202 +0x0 main.main.func1() /home/yury/race.go:19 +0xb7 ...<p>Советую периодически использовать флаг -race для выявления проблем с гонками. В продакшене же он существенно замедляет работу вашего приложения и не рекомендуется.</p>
12 ================== WARNING: DATA RACE Write at 0x00c00009c150 by goroutine 8: runtime.mapassign_faststr() /usr/lib/go-1.13/src/runtime/map_faststr.go:202 +0x0 main.main.func2() /home/yury/race.go:19 +0xb7 Previous write at 0x00c00009c150 by goroutine 7: runtime.mapassign_faststr() /usr/lib/go-1.13/src/runtime/map_faststr.go:202 +0x0 main.main.func1() /home/yury/race.go:19 +0xb7 ...<p>Советую периодически использовать флаг -race для выявления проблем с гонками. В продакшене же он существенно замедляет работу вашего приложения и не рекомендуется.</p>
13  
13