Являются ли переменные, назначенные из одного потока, "потокобезопасными" при чтении из другого потока, если используется EventWaitHandel?
Если я создаю переменную в одном потоке, то блокирую, используя ManualResetEvent
"s WaitOne()
метод, пока другой поток не присвоит значение той же переменной и не EventWaitHandel
, Когда я прихожу, чтобы прочитать переменную в первом потоке, я гарантированно всегда получаю значение, только что назначенное другим потоком?
(Боюсь, я не смог получить значение из кэша ЦП из-за некоторой оптимизации, так как я не использовал никаких барьеров памяти, насколько мне известно).
например
var str = "multi-threading is hard!";
var mre = new ManualResetEvent(false);
Task.Factory.StartNew(() =>
{
str = Console.ReadLine();
mre.Set();
));
mre.WaitOne();
Console.WriteLine(str);
2 ответа
Эти инструкции не будут переупорядочены, что означает, что в производящем потоке присвоение поля всегда будет происходить до того, как будет передан сигнал дескриптору, а в потоке-потребителе поле всегда будет считываться после того, как будет передан дескриптор.
Если любая из этих двух пар инструкций может быть переупорядочена (например, если второй поток может прочитать поле до того, как дескриптор будет сигнализирован), то вы не увидите правильное значение.
WaitOne()
вводит неявный барьер памяти, давая вам семантику приобретения-выпуска, в которой вы нуждаетесь.
Брайан Гидеон и Ханс Пассант составили прекрасный список нескольких классов в среде.NET, которые вводят неявные барьеры памяти: генераторы барьеров памяти
Дополнительная информация: приобретать и выпускать семантику / приобретать и выпускать ограждения
Ваша переменная является захваченной переменной, то есть компилятор превращает эту локальную переменную в поле класса, сгенерированного компилятором, потому что вы используете его в лямбда-выражении. Afaik, эти сгенерированные компилятором поля не помечены volatile
чтобы они могли быть кэшированы.
РЕДАКТИРОВАТЬ: Действительно, поле не является изменчивым.
Вы можете определенно предотвратить кэширование, написав свой собственный класс, чтобы компилятору не приходилось его создавать. Однако это, конечно, мешает краткости вашего кода.