2016-11-04 2 views
4

Я новичок в многопоточном программировании, и я нашел std::atomic в C++ 11.Как я могу правильно увеличить C++ 11 std :: atomic?

Итак, я попытался выяснить, сколько времени выполняются атомными операциями.

Я попробовал этот код:

using namespace std; 
using namespace std::chrono; 

constexpr int NUM_THREADS = 8; 
constexpr int LIMIT = 100000; 

atomic<int> sum = 0; 

void foo(int idx) { 
    while (true) { 
     if (sum.load() >= LIMIT) { 
      return; 
     } 
     sum.fetch_add(1); 
    } 
} 

с main:

int main(void) { 
    thread threads[NUM_THREADS]; 

    auto start = high_resolution_clock::now(); 

    for (int i = 0; i < NUM_THREADS; i++) { 
     threads[i] = thread(&foo, i); 
    } 

    for (int i = 0; i < NUM_THREADS; i++) { 
     threads[i].join(); 
    } 
    auto du = high_resolution_clock::now() - start; 

    cout << "computing time\t\t" << duration_cast<milliseconds>(du).count() << "ms" << endl; 
    cout << "number of thread\t" << NUM_THREADS << endl; 
    cout << "sum\t\t\t" << sum << endl; 

    return 0; 
} 

Но sum не всегда совпадает с LIMIT.

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

Как я могу получить правильный результат с этим main?

(ну, эта версия будет делать sum и LIMIT равны, но я думаю, что это не лучший способ ...)

void foo(int idx) { 
    for (int i = 0; i < LIMIT/NUM_THREADS; i++) { 
     sum.fetch_add(1); 
    } 
} 
+2

Первый из них не работает, потому что ваш чек и ваш инкремент являются двумя отдельными операциями, поэтому 2 потока могут читать что-то ниже LIMIT, и оба решают увеличить. – Borgleader

+0

Ваша проблема заключается не в том, как увеличить атом - ваша проблема заключается в том, как атомизировать последовательность операций * two *. – Hurkyl

+2

Вам необходимо либо http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange, либо mutex. –

ответ

5

Как уже было сказано в комментариях, ваша проблема в том, что переменная изменяется другим потоком между временем загрузки и временем, когда вы его увеличиваете.

Вы можете изменить свой цикл, например. как это исправить:

while (true) { 
    auto current = sum.load();   
    if (current >= LIMIT) { 
     return; 
    } 
    auto next = current + 1; 
    sum.compare_exchange_strong(current, next)); 
} 
+0

ничего себе. сбежал с cppreference.com. Большое спасибо! –

-1

operator++ атомична на атомарных типов, поэтому все, что вам нужно сделать, это:

void foo(int idx) { 
    while (true) { 
     if (sum++ >= LIMIT) { 
      return; 
     } 
    } 
} 

После приращения один из потоков sum в LIMIT, остальные из потоки также будут видеть, что значение больше или равно LIMIT и возвращает. Здесь существует один риск: если число потоков больше std::numeric_limits<int>::max() - LIMIT, то один из последующих потоков будет увеличивать sum за максимальным значением для int. До тех пор, пока LIMIT разумно <g> это не будет проблемой.

Смежные вопросы