Что происходит, если параллельные процессы записывают в глобальную переменную одно и то же значение?
Мне просто интересно, есть ли вероятность коррупции в результате записи одного и того же значения в глобальную переменную одновременно. Мой мозг говорит мне, что в этом нет ничего плохого, потому что это просто место в памяти, но я полагаю, что мне стоит проверить это предположение.
У меня есть параллельные процессы записи на глобальную карту var linksToVisit map[string]bool
, Карта фактически отслеживает, какие ссылки на веб-сайте необходимо сканировать.
Однако может случиться так, что параллельные процессы могут иметь одну и ту же ссылку на своих соответствующих страницах, и поэтому каждый из них помечает эту же ссылку как true
одновременно. В этом случае нет ничего плохого в том, чтобы НЕ использовать замки, верно? ПРИМЕЧАНИЕ. Я никогда не изменяю значение обратно на false
так что либо ключ существует, и его значение равно true, либо его не существует.
Т.е.
var linksToVisit = map[string]bool{}
...
// somewhere later a goroutine finds a link and marks it as true
// it is never marked as false anywhere
linksToVisit[someLink] = true
4 ответа
Что происходит, если параллельные процессы записывают в глобальную переменную одно и то же значение?
Результаты гонки данных не определены.
Запустите детектор данных Go.
Рекомендации:
Доброкачественные гонки данных: что может пойти не так?
Блог Go: Представляем Go Race Detector
Неправильное использование карты
В Go 1.6 среда выполнения добавила облегченное обнаружение одновременных злоупотреблений картами. В этом выпуске улучшен этот детектор благодаря поддержке обнаружения программ, которые одновременно записывают и перебирают карту.
Как всегда, если одна программа записывает на карту, никакая другая программа не должна читать (включая итерации) или записывать карту одновременно. Если среда выполнения обнаруживает это состояние, она печатает диагноз и вылетает из программы. Лучший способ узнать больше о проблеме - запустить программу под детектором гонки, который будет более надежно определять гонку и давать больше подробностей.
Например,
package main
import "time"
var linksToVisit = map[string]bool{}
func main() {
someLink := "someLink"
go func() {
for {
linksToVisit[someLink] = true
}
}()
go func() {
for {
linksToVisit[someLink] = true
}
}()
time.Sleep(100 * time.Millisecond)
}
Выход:
$ go run racer.go
fatal error: concurrent map writes
$
$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00c000078060 by goroutine 6:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:190 +0x0
main.main.func2()
/home/peter/gopath/src/racer.go:16 +0x6a
Previous write at 0x00c000078060 by goroutine 5:
runtime.mapassign_faststr()
/home/peter/go/src/runtime/map_faststr.go:190 +0x0
main.main.func1()
/home/peter/gopath/src/racer.go:11 +0x6a
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:14 +0x88
Goroutine 5 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:9 +0x5b
==================
fatal error: concurrent map writes
$
Блокировки лучше использовать, если вы изменяете одно и то же значение одновременно, используя несколько процедур go. Так как мьютекс и блокировки используются всякий раз, когда это необходимо для обеспечения доступа к значению, когда другая функция изменяет его так же, как запись в таблицу базы данных при доступе к той же таблице.
На ваш вопрос об использовании карт с разными ключами в Go не желательно:
Типичное использование карт не требовало безопасного доступа из нескольких программ, и в тех случаях, когда это требовалось, карта, вероятно, была частью некоторой более крупной структуры данных или вычислений, которые уже были синхронизированы. Поэтому требование, чтобы все операции с картами захватывали мьютекс, замедляло бы большинство программ и повышало безопасность немногих.
Доступ к карте небезопасен, только когда происходят обновления. Пока все подпрограммы только читают - ищут элементы на карте, включая итерацию по ней с помощью цикла for - и не изменяют карту, назначая элементам или удаляя, они могут безопасно получить доступ к карте одновременно без синхронизации.
Так что в случае обновления карт это не рекомендуется. Для получения дополнительной информации проверьте FAQ о том, почему карты операций не определены атомарно.
Также замечено, что если вы действительно хотите пойти, должен быть способ синхронизировать их.
Карты не безопасны для одновременного использования: не определено, что происходит, когда вы читаете и пишете в них одновременно. Если вам нужно читать и записывать на карту из одновременного выполнения подпрограмм, доступ должен быть обеспечен каким-то механизмом синхронизации. Один из распространенных способов защиты карт - это sync.RWMutex.
Начиная с версии 1.6, одновременная запись на карту вызовет panic
, Использовать sync.Map
синхронизировать доступ.
См. Реализацию присвоения значения карты: https://github.com/golang/go/blob/fe8a0d12b14108cbe2408b417afcaab722b0727c/src/runtime/hashmap.go#L519
Запись с одновременной карты не подходит, поэтому вы, скорее всего, получите фатальную ошибку. Поэтому я думаю, что замок должен быть использован