Неужели невозможно, чтобы загрузка получения возвращала `1` при выходе из циклов в других потоках?C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Неужели невозможно, чтобы загрузка получения возвращала `1` при выходе из циклов в других потоках?

Сообщение Anonymous »

Рассмотрим этот пример:

Код: Выделить всё

#include 
#include 
#include 
int main() {
std::atomic strong = {3};
std::atomic weak = {1};
auto t1 = std::thread([&]() {
int expected = 1;
if (weak.compare_exchange_strong(expected, 255,
std::memory_order::acquire,
std::memory_order::relaxed) == true) {
bool unique = strong.load(std::memory_order::acquire) == 1; // #1
weak.store(1, std::memory_order::relaxed); // #4
assert(unique == false);
}
});
auto t2 = std::thread([&]() {
auto val = weak.load(std::memory_order::relaxed);
while (true) {
if (val == 255) {
std::this_thread::yield();
val = weak.load(std::memory_order::relaxed);
continue;
}
if (weak.compare_exchange_strong(
val, val + 1, std::memory_order::acquire,
std::memory_order::relaxed) == true) {
break;
}
}
strong.fetch_sub(1, std::memory_order::release); // #2
});
auto t3 = std::thread([&]() {
auto val = weak.load(std::memory_order::relaxed);
while (true) {
if (val == 255) {
std::this_thread::yield();
val = weak.load(std::memory_order::relaxed);
continue;
}
if (weak.compare_exchange_strong(
val, val + 1, std::memory_order::acquire,
std::memory_order::relaxed) == true) {
break;
}
}
strong.fetch_sub(1, std::memory_order::release); // #3
});
t1.join();
t2.join();
t3.join();
}
Предполагая, что операция CAS в t1 завершается успешно, это означает, что циклы в t2 и t3 не могут завершиться, за исключением того, что один из них читает #1, а другой читает значение, записанное операцией RMW, которая читает #1. Вопрос в том, может ли #0 прочитать 1, и утверждение не удастся. Предположим, что #0 читается как 1, поскольку начальное значение Strong равно 3, это означает, что #2 и #3 происходят до #0, что, в свою очередь, подразумевает, что загрузочная часть слабого в t2 и t3 происходит до #1. Согласно [intro.races] p13

Если вычисление значения A атомарного объекта M происходит до операции B, которая изменяет M, то A берет свое значение из побочного эффекта X на M, где X предшествует B в порядке модификации M.
не виден для части загрузки слабого в t1 и t3, поэтому цикл в t2 и t3 не может выйти; это означает, что #2 и #3 не могут быть достигнуты соответствующими потоками управления в их соответствующих потоках. Это противоречит предположению, что #0 читается как 1, поскольку #2 и #3 не будут выполнены за время существования программы; в противном случае это нарушит [intro.races] p10

Значение атомарного объекта M, определенное оценкой B, представляет собой значение, сохраненное неким неуказанным побочным эффектом A, который изменяет M, где B не происходит до A.

В более общем смысле, любой поток, утверждающий, что его операция RMW на Strong будет прочитана #1 невозможно, поскольку его операция CAS на слабом не ожидает 255, а #4 не виден для части загрузки, поэтому его цикл не может выйти.
Итак, #0 не может прочитать 1 и 2; однако он может прочитать только начальное значение 3. Верен ли мой анализ? На самом деле, это упрощенный вопрос из моего исследования реализации Arc в Rust, в частности, относительно метода is_unique (строка 2585) и метода понижения версии (строка 1735). Если цель, как описано в комментарии, состоит в том, чтобы проверить, содержит ли поток уникальный сильный, я думаю, что порядок памяти, используемый для этого случая, является чрезмерно строгим, а порядок памяти для слабого хранилища, который записывает 1, может быть, по крайней мере, ослаблен, IIUC.

Подробнее здесь: https://stackoverflow.com/questions/798 ... ther-threa
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»