Почему мои атомные операции происходят в неожиданном порядке? - PullRequest
0 голосов
/ 27 февраля 2012

Я не понимаю, почему это происходит. Возьмите следующий фрагмент кода псевдо:

volatile unsigned long count = 0;
volatile unsigned long sum = 0;

void ThreadFunction() {
    InterlockedIncrement(&count);
    InterlockedExchangeAdd(&sum, rand());
}

int main() {
    for (int i = 0; i < 10; ++i) {
        // This is the problematic instruction
        InterlockedExchange(&count, 0);
        InterlockedExchange(&sum, 0);

        std::vector<boost::thread> threads(i);
        for (int j = 0; j < i; ++j)
            threads[j] = boost::thread(ThreadFunction);

        while (count != i)
            Sleep(0);
    }
}

При самом первом запуске программы, когда i = 0, атомный обмен на sum из основного потока обычно происходит после завершенного порожденного потока. Операции на count всегда выполняются в правильном порядке.

Это происходит только один раз; он выполняет операции в правильном порядке для оставшейся части цикла. Это не всегда происходит, но обычно происходит. Если я ворвусь в отладчик или перестану спать до атомарного добавления, инструкции будут выполнены в правильном порядке.

В любом случае, результатом является то, что значение, записанное потоком, заменяется на 0, которое должно было произойти еще до того, как поток был запущен.

Почему это происходит? Почему я не могу полагаться на процессор для выполнения атомарных инструкций, которые я дал ему в том порядке, в котором я их дал? Разве атомарная операция не подразумевает барьер памяти для предотвращения переупорядочения?

Примечание: это происходит в Visual Studio 2010. У меня нет других версий для тестирования.

1 Ответ

7 голосов
/ 27 февраля 2012

Атомная операция является атомарной в том смысле, что она не прерываема. Он либо не будет выполнен, либо полностью завершится, если другой поток не заглянет в него.

Это не означает, что двадцать атомарных операций всегда будут происходить в одном и том же порядке, это все еще подвержено капризам многопоточности.

В зависимости от того, как быстро запускаются ваши потоки, они могут быть упорядочены как main, thrd1, main, thrd2, main, thrd3, ... или main, main, main, main, thrd1, main, main, thrd2, ... или любая другая комбинация (хотя я вполне уверен, main всегда будет первым просто потому, что его атомарная операция выполняется перед запуском любые темы).

...