Является ли сравнение атомарной операцией? - PullRequest
3 голосов
/ 24 апреля 2011

Является ли следующее сравнение атомарным действием? Т.е. можно ли его уменьшить до одной инструкции процессора?

char flag = 2;

for(;;)
{
    if (!flag) // <-- this
        break;

    // sleep
}

Вот что я делаю:

int main()
{
    sf::Mutex Mutex;
    char flag = 2;

    coordinatorFunction(flag);

    for(;;)
    {
        if (!flag)
            break;

        // sleep
    }
}

void workerFunction(void* a)
{
    char* p = static_cast<char*>(a);

    // work

    GlobalMutex.Lock();
    --*p;
    GlobalMutex.Unlock();
}

void coordinatorFunction(char& refFlag)
{
    sf::Thread worker1(&workerFunction, &refFlag);
    sf::Thread worker2(&workerFunction, &refFlag);

    worker1.Launch();
    worker2.Launch();
}

Ответы [ 8 ]

7 голосов
/ 24 апреля 2011

Это неправильный путь.

Ваш основной поток сжигает циклы процессора так быстро, как только может, ничего не делая, только ожидая, пока flag достигнет нуля. Этот тест будет проваливаться при каждой попытке, кроме последнего. Вместо того, чтобы делать это таким образом, используйте функцию «соединения», которую ваши объекты потока, скорее всего, должны заставить основной поток приостанавливать до тех пор, пока все рабочие не завершат работу.

Таким образом, не случайно , вам будет все равно, если тест атомарный, потому что он вам вообще не понадобится.

2 голосов
/ 24 апреля 2011

Нет.Насколько я знаю, C ++ не дает никаких гарантий относительно того, что является атомарным, а что нет - это зависит от платформы.Даже если ваша платформа гарантирует, что сравнение может быть выполнено атомарно, нет никакой гарантии, что ваш компилятор C ++ выберет эту инструкцию.

Однако на практике сравнение простого типа значения, такого как char, int,float и т. д., вероятно, будут атомными.Тем не менее, вам все же нужно знать о возможном переупорядочении инструкций как на уровне компилятора, так и на уровне процессора.В этом случае это может не иметь значения, но в общем случае это может и имеет значение.Вам также нужно знать, что ветвь сравнения-потом-ветвления не является атомарной, даже если сравнение - так что 2 потока могут оба ввести один и тот же блок кода, если вы пытаетесь использовать флаг для регулирования этого доступа.

Если вам нужны надлежащие гарантии, существуют различные блокированные функции в Windows и атомарные встроенные функции в gcc.

2 голосов
/ 24 апреля 2011

В C ++ ничто не может быть атомарным.

2 голосов
/ 24 апреля 2011

Ни одна из операций C ++ гарантированно не является атомарной.

1 голос
/ 24 апреля 2011

Нет .

Сравнение включает чтение и фрагментов данных , а также выполнение фактического сравнения. Данные могут изменяться между инструкциями чтения и сравнения , поэтому они не являются атомарными.

Однако, поскольку вы сравниваете на равенство, _InterlockedCompareExchange instrinsic (то есть для инструкций lock cmp xchg в x86) может делать то, что вам нужно, хотя это потребует замены данных.

1 голос
/ 24 апреля 2011

Сравнение не является атомарным, потому что для этого требуется несколько инструкций машинного языка (загрузка из памяти в регистр и т. Д.). Также из-за гибкости модели памяти и кэширования потока, выполняющего тест, он может не «видеть» результатыо другом потоке.

Вы можете безопасно выполнить простой тест, если переменная помечена как volatile, но это будет зависеть от платформы.Как отмечали другие, сам C ++ на это не дает никаких гарантий.

1 голос
/ 24 апреля 2011

Нет, C ++ не дает никаких гарантий, что любая операция является атомарной.Код в вашем вопросе вполне может быть скомпилирован в загрузку из памяти в регистр, который сам может принять несколько инструкций, а затем выполнить тест.

0 голосов
/ 27 апреля 2011

Вопрос, который вы должны задать: «это - атомный»? Вот и все, что здесь имеет значение. Вы хотите что-то сделать, когда флаг достигнет 0.

Вам плевать на этот сценарий:

1. Main thread reads flag, and it is 1.
2. Worker changes flag with --
3. Main thread doesn't see that flag is actually 0.

Поскольку через 1 нс основной поток зацикливается и пытается снова.

Вас волнует, что это не атомарное и два потока, изменяющие его одновременно, пропустят уменьшение:

1. Thread A reads flag, flag is 2
2. Thread B reads flag, flag is 2
3. Thread A decrements its copy of flag, 2, and writes to flag, flag is 1
4. Thread B decrements its copy of flag, also 2, and writes to flag, flag is 1.

Вы потеряли декремент. вы хотите использовать __ sync_fetch_and_sub (& flag, 1), который будет атомарно уменьшать флаг.

Наконец, вращение вокруг сна - не лучший способ сделать это. Вы хотите либо подождать при условии, либо подождать сигнал . Пусть рабочие потоки повышают условие или сигнал, когда они понимают, что у них уменьшен флаг до 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...