2014-10-30 3 views
5

Итак, я начинаю знакомиться с типами C++ 11 <atomic>. Раньше, когда у меня был атомный флаг, я обычно просто блокировал мьютекс, прежде чем обращаться к нему. Общей потребностью было бы проверить, равен ли флаг false, и если да, то атомически установите его на true, а затем сделайте что-нибудь. Таким образом, в основном это будет достигнуто, как это, где flag простой bool:Основное использование условных выражений с std :: atomic <T>

{ 
    std::lock_guard<std::mutex> lock(my_mutex); 
    if (!flag) 
    { 
     flag = true; 
     // do something; 
    } 
} 

Итак, теперь я пытаюсь выяснить, как то же самое может быть достигнуто с <atomic>. docs говорят, что оператор присваивания и operator T атомного типа являются атомарными операциями. Однако, если я изменю flag к std::atomic<bool>, я думаю, я не могу просто сказать:

if (!flag) 
{ 
    flag = true; 
    // do something 
} 

... потому что даже если выражение (!flag) атомная, и назначение flag = true атомарный, нет ничего, чтобы предотвратить еще один поток от изменения флага между этими двумя утверждениями.

Итак, если я правильно понимаю, здесь, единственное правильное использование - на всех - из условных с атомарными типами, где результат условными может изменить атомные переменный, чтобы использовать Compare и операцию свопа ? Я прав?

Таким образом, я должен был бы сказать:

bool expected = false; 
if (flag.compare_exchange_weak(expected, true)) 
{ 
    // do something 
} 

Я правильно в моем понимании здесь?

+2

['std :: atomic_flag'] (http://en.cppreference.com/w/cpp/atomic/atomic_flag) существует для конкретного варианта использования, который вы описываете, и тем более гарантируется, бесплатно на всех платформах. Вы бы использовали метод 'test_and_set', который является атомарным.Вы также можете использовать 'std :: atomic ' и выполнить на нем 'fetch_add', который является атомарным и даст вам предыдущее значение во время выполнения приращения (это часто бывает быстрее, чем CAS на большинстве архитектур, хотя я предполагаю, что не быстрее, чем 'std :: atomic_flag', что было бы предпочтительным в этом случае). – Cameron

+0

Правильно - но кажется, что нет возможности просто атомизировать «проверку» значения атомного флага (без его настройки) - или есть? Документы не определяют 'operator bool' или что бы то ни было, что бы включить выражение' if (flag) '. Я понимаю, что не задал этого требования в своем вопросе - мне просто интересно. Говоря о том, почему ... почему * нет * есть способ просто проверить атомный флаг, не устанавливая его? – Siler

+0

Правильно, вы должны установить его, чтобы протестировать его (что эквивалентно вашему образцу кода, как он написан в данный момент, за исключением того, что он является атомарным). Если вам нужно протестировать его по отдельности, я предлагаю использовать метод 'fetch_add' или' compare_exchange_strong' (который намного понятнее и [так же быстро, как 'fetch_add' (' lock xadd') на x86] (http: // www.agner.org/optimize/instruction_tables.pdf)). – Cameron

ответ

3

Если у вас есть несколько потоков, которые работают с тем же кодом, что и для этого флип, да - вам нужно будет использовать compare_exchange_weak() или compare_exchange_strong() по той причине, которую вы предлагаете (вероятно, предпочитаете сильные).

Однако не следует сказать, что это только правильное использование условных обозначений с атомикой. Если у меня есть, скажем, одну нить, которая только когда-либо читает атомное и тот, который записывает на него, это вполне разумно использовать их простой путь ... например:

std::atomic<bool> done{false}; 

// thread 1 
while (!done) { 
    .... 
} 

// thread 2 
stop() { done = true; } 

Там нет никакой причины для меня, чтобы сделать done.compare_exchange_strong(expected, true) есть. Это слишком много. Это действительно в каждом конкретном случае.

+0

Из кода OP видно, что 'compare_exchange_ {weak, strong}' подходит в этом случае. Вопрос действительно квалифицирует слово «только» с условием, что результат сравнения будет использоваться для определения того, происходит ли присвоение. Тем не менее, хороший тщательный ответ для более широкого набора вариантов использования. –

1

Да.

Это, как говорится, вы можете предпочесть compare_exchange_strong для этого, если только вы не находитесь в плотной петле. Это может привести к снижению производительности, но гарантированно даст вам ожидаемый результат (который compare_exchange_weak не является).

0

Очевидное примечание: семантика замка совсем не похожа на атомную проверку, так как в этом случае другой поток ждет, пока блокировка не будет освобождена и будет всегда выполнять свои действия после того, как будет получен замок. Итак, строго говоря, два примера кода не эквивалентны.