У меня есть следующая программа. Соответствующая информация:
Есть 3 атомарные переменные x,y,z
3 потока записи: каждый поток считывает все 3 значения x,y,z и обновляет ровно 1 переменную x, y или z. Значения сохраняются в значениях1, значениях2, значениях3
2 потока чтения: каждый пытается прочитать значения x,y,z и сохранить их внутри значений4 и значений5
Между всеми потоками происходит гонка данных, поскольку нет синхронизации. Однако в каждом потоке значение обновляемой переменной единообразно (например, поток 1 всегда будет видеть значения x как 0, 1, 2, 3, ..., 9)
Вы можете видеть, что для каждого потока записи принадлежащая ему переменная имеет постоянное изменение от 0 до>9.
Это неопределенное поведение, поэтому технически может произойти все что угодно. Однако я заметил две вещи:
Внутри каждого потока и для каждой переменной ее значение никогда не «возвращается во времени». Например, если переменная обновляется как «1-2-3-4-5-...-9-10», и если поток в первый раз загружает переменную, он видит «4», тогда он никогда не увидит 1, 2 или 3. Он может только стоять на месте (застрял при виде 4), или продолжить и застрять где-то на 5-9, или продолжить до конца (см. 10)
В группе из 3 потоков (скажем, поток 1->3), если последние значения x, которые они видят, равны (3,2,0), то поток 1 устанавливает x = 4, следующие возможные значения, которые видят потоки 3: (4,2,0), (4,2,1), (4,2,2), (4,2,3), (4,2,4), (4,3,0), ..., (4,4,4). Наиболее заметно то, что если поток 2 видит x == 4, вполне возможно, что поток 3 видит x == 0
Что именно в аппаратном или программном обеспечении вызывает такое поведение? Я могу лишь предположить несколько объяснений, но не уверен, верны они или нет.
Значения не уменьшаются, поскольку считываются из кэша. И значение каждой загрузки — это только последнее значение, записанное в кеш.
2 потока могут видеть разные значения, поскольку их кеш может обновляться в разном порядке.
Некоторые значения отсутствуют из-за переключения контекста и выхода (например, после чтения x == 2, потока 4 std::yield и пробуждаются только после завершения потока 1 и x == 10).
У меня есть следующая программа. Соответствующая информация: [list] [*]Есть 3 атомарные переменные x,y,z
[*]3 потока записи: каждый поток считывает все 3 значения x,y,z и обновляет ровно 1 переменную x, y или z. Значения сохраняются в значениях1, значениях2, значениях3
[*]2 потока чтения: каждый пытается прочитать значения x,y,z и сохранить их внутри значений4 и значений5
[*]Между всеми потоками происходит гонка данных, поскольку нет синхронизации. Однако в каждом потоке значение обновляемой переменной единообразно (например, поток 1 всегда будет видеть значения x как 0, 1, 2, 3, ..., 9)
[/list] Вы можете видеть, что для каждого потока записи принадлежащая ему переменная имеет постоянное изменение от 0 до>9. Это неопределенное поведение, поэтому технически может произойти все что угодно. Однако я заметил две вещи: [list] [*]Внутри каждого потока и для каждой переменной ее значение никогда не «возвращается во времени». Например, если переменная обновляется как «1-2-3-4-5-...-9-10», и если поток в первый раз загружает переменную, он видит «4», тогда он никогда не увидит 1, 2 или 3. Он может только стоять на месте (застрял при виде 4), или продолжить и застрять где-то на 5-9, или продолжить до конца (см. 10) [*]В группе из 3 потоков (скажем, поток 1->3), если последние значения x, которые они видят, равны (3,2,0), то поток 1 устанавливает x = 4, следующие возможные значения, которые видят потоки 3: (4,2,0), (4,2,1), (4,2,2), (4,2,3), (4,2,4), (4,3,0), ..., (4,4,4). Наиболее заметно то, что если поток 2 видит x == 4, вполне возможно, что поток 3 видит x == 0 [/list] Что именно в аппаратном или программном обеспечении вызывает такое поведение? Я могу лишь предположить несколько объяснений, но не уверен, верны они или нет. [list] [*]Значения не уменьшаются, поскольку считываются из кэша. И значение каждой загрузки — это только последнее значение, записанное в кеш. [*]2 потока могут видеть разные значения, поскольку их кеш может обновляться в разном порядке. [*]Некоторые значения отсутствуют из-за переключения контекста и выхода (например, после чтения x == 2, потока 4 std::yield и пробуждаются только после завершения потока 1 и x == 10). [/list] #include #include #include #include std::atomic x(0),y(0),z(0); std::atomic go(false); unsigned const loop_count=10; struct read_values { int x,y,z; }; read_values values1[loop_count]; read_values values2[loop_count]; read_values values3[loop_count]; read_values values4[loop_count]; read_values values5[loop_count]; void increment(std::atomic* var_to_inc,read_values* values) { while(!go) std::this_thread::yield(); for(unsigned i=0; istore(i+1,std::memory_order_relaxed); std::this_thread::yield(); } } void read_vals(read_values* values) { while(!go) std::this_thread::yield(); for(unsigned i=0; i