#20.? golang go tun -race Обнаружение состояние гонки средствами компилятора - WARNING: DATA RACE
Primary tabs
Forums:
Обнаружение гонки данных
Чтобы обнаружить гонку данных средствами golang-а, можно запустить программу с флагом -race, например:
go run -race test.go
где test.go - имя файла с исходным кодом
Например, если мы запустим такой код:
package main
import (
"fmt"
"time"
)
func incIt(a *int) {
*a += 1 // инкрементируем
fmt.Print(*a, "_")
}
func main() {
a := 1
go incIt(&a)
go incIt(&a)
// Ждем завершения горутин
time.Sleep(500 * time.Millisecond) // полсекунды должно хватить
}
то получим ответ вида:
2_==================
WARNING: DATA RACE
Read at 0x00c00011a028 by goroutine 9:
main.incIt()
/var/www/go-test/test.go:9 +0x30
main.main.gowrap2()
/var/www/go-test/test.go:21 +0x33
Previous write at 0x00c00011a028 by goroutine 8:
main.incIt()
/var/www/go-test/test.go:9 +0x44
main.main.gowrap1()
/var/www/go-test/test.go:20 +0x33
Goroutine 9 (running) created at:
main.main()
/var/www/go-test/test.go:21 +0x10a
Goroutine 8 (running) created at:
main.main()
/var/www/go-test/test.go:20 +0xa4
==================
3_Found 1 data race(s)
exit status 66
-- WARNING: DATA RACE тут как раз и обозначает, что состояние гонки зафисировано
Как компилятор обнаруживает состояние гонки
1. Этап компиляции: Инструментирование
Когда мы добавляем флаг -race, компилятор Go переходит в специальный режим. Вместо обычного машинного кода он генерирует «инструментированный» код, где:
- Перехват обращений к памяти: Каждое чтение или запись переменной в коде дополняется вызовом функции из библиотеки ThreadSanitizer (например, __tsan_read или __tsan_write).
- Регистрация событий: Компилятор также вставляет вызовы при входе и выходе из функций, а также при операциях синхронизации (захват мьютекса, отправка в канал - если такие операции вообще есть).
2. Этап выполнения: Алгоритм Vector Clocks
Когда программа запущена, детектор следит за всеми обращениями к памяти и использует логические часы (Vector Clocks) для отслеживания цепочки событий.
Детектор хранит для каждого адреса памяти (размером в 8 байт) небольшую «теневую» запись (shadow memory), где записано:
- Кто последний обращался к этой памяти (ID горутины).
- Когда это было (время по логическим часам).
- Тип операции (чтение или запись).
Как принимается решение о гонке:
Пример ситуации:
- Горутина №2 хочет записать данные по адресу X.
- Детектор проверяет теневую память этого адреса и видит, что последней там была Горутина №1 (делала чтение или запись).
- Детектор ищет связь: «Было ли событие синхронизации (мьютекс или канал) между Горутиной 1 и Горутиной 2?».
- Если связи нет, и одна из операций — запись, то это Data Race. Программа немедленно печатает отчет.
-- для последнего примера код выша получим вывод вида:
- Log in to post comments
- 52 reads