В настоящее время у меня есть механизм, в котором 2 потока читают/записывают одни и те же данные; хотя и не одновременно; и не используйте никаких механизмов синхронизации.
Возможно, самым простым примером будет (псевдокод)
x = 5;
startThread(changeX).join()
assert(x != 5)
таким образом, мы можем быть уверены, что рассматриваемый поток обработал x
до того, как он будет прочитан стартовым потоком.
Является ли это неопределенным поведением (поскольку x может быть оптимизирован) или оно безопасно?
Мой опыт работы с С++ и мои исследования до сих пор говорят о небезопасности, но я не вижу ничего определенного.
@Prix, давайте согласимся, что нет .... это все еще безопасно (например, может ли компилятор что-то оптимизировать для меня)?
Существует также вероятность того, что вы прочитаете его, прежде чем писать в него, нежелательно ли такое поведение? если да, то я бы сказал синхронизация, если ее нет, то я не вижу ничего, что могло бы вас удержать. Компилятор не будет создавать блокировки за вас. если компилятор считает код бесполезным, он может его оптимизировать. Например, если вы напишете var test = true;
и скомпилируете его при выпуске.
Использование вами слова «по-видимому» вызовет некоторые сомнения в том, что они никогда не сталкивались.
@UKMonkey То, как этот код написан, родительский поток блоки в ожидании завершения дочернего потока. Часть субoptimal заключается в том, что создается новый поток вместо выбора бездействующего потока из пула потоков. Вы можете использовать, например, Task.Run(()=>changeX(...)).Wait();
для использования потока из пула или await Task.Run....
для асинхронного ожидания.
Для меня ключевое слово «кажется». Мы можем вечно тестировать многопоточный код, не видя проблем, а потом они возникают, как только наше приложение оказывается в какой-то другой среде. Скорее всего, мы просто не будем знать, когда это произойдет, что позволит дольше вызывать проблемы. Потом кто-то что-то сообщает, но мы не можем это повторить. Затем мы повторяем это, но никто не может понять, в чем проблема.
@PanagiotisKanavos прав, но это не говорит о том, безопасно это или нет.
Слишком верно - мое сомнение в коде, на который я смотрю, не должно влиять на основы вопроса ... позвольте мне настроить.
@UKMonkey это так - другой поток работает нет, когда вы звоните assert(x != 5)
. У кода другая проблема — что делает changeX
? Ints - это типы значений, поэтому changeX
не может изменить X, если он не передан по ссылке.
@UKMonkey, что такое x
? Переменная? Поле? Имущество? Зачем использовать такую тему вместо, например, x = await Task.Run(()=>someHeavyWork()
?
Переменная-член (класс) @PanagiotisKanavos - в С++ компилятор мог бы разумно взглянуть на это; и не выполнить утверждение, потому что x не мог быть изменен данным потоком, и вокруг x нет синхронизации, чтобы сообщить компилятору, что он может измениться. Я просто недостаточно хорошо знаю спецификацию С#, чтобы увидеть, можно ли применить те же оптимизации
При условии, что вы пишете псевдокод, я могу сказать вам следующее:
1 - Эмпирическое правило: всякий раз, когда 2 потока выполняют запись/чтение одной и той же переменной, вам необходимо учитывать использование потокобезопасных структур, и вы всегда должны использовать какую-то синхронизацию между потоками.
2 - Хотя в вашем случае вы работаете с атомарными переменными, у вас не будет проблем при выполнении чтения/записи, но здесь у вас может быть состояние гонки, и у вас могут быть случайные выходные данные в зависимости от того, как Windows решает обрабатывать потоки .
Принимая во внимание обе вещи, я настоятельно рекомендую вам использовать некоторую синхронизацию, чтобы не получать случайные результаты в зависимости от того, как Windows решает обрабатывать ваши потоки. Ваше чутье в С++ говорит вам что-то, потому что здесь что-то происходит
Реальный вопрос в том, есть ли шансы на столкновение двух потоков? если есть шанс, я бы сказал, что вам нужно синхронизировать