Подтвердить что ты не робот

Самый простой способ реализовать общий целочисленный счетчик в С++ 11 без мьютексов:

Предположим, что мы имеем следующий код, который подсчитывает количество случаев:

int i=0;
void f() {
   // do stuff  . . .
   if(something_happens) ++i;
}

int main() {
    std::vector<std::thread> threads;
    for(int j = 0; j< std::thread::hardware_concurrency(); ++j) {
        threads.push_back(std::thread(f));
    }

    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread_join));
    std::cout << "i = " << i << '\n';
}

В настоящее время существует четкое условие гонки на i. Используя С++ 11, что является (1) самым простым методом для устранения этого состояния гонки и (2) самым быстрым методом?, предпочтительно без использования мьютексов. Спасибо.

Обновление. Используя комментарий для использования atomics, я получил рабочую программу, которая компилируется в Intel Compiler, версия 13:

#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
#include <algorithm>

std::atomic<unsigned long long> i = 0;

void f(int j) {
    if(j%2==0) {
        ++i;
    }  
}

int main() {
    std::cout << "Atomic i = " << i << "\n";
    int numThreads = 8; //std::thread::hardware_concurrency() not yet implemented by Intel
    std::vector<std::thread> threads;
    for(int k=0; k< numThreads; ++k) {
        threads.push_back(std::thread(f, k));
    }

    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
        std::cout << "Atomic i = " << i << "\n";
    }
4b9b3361

Ответ 2

Мы решили аналогичную проблему, объявив массив [nThreads], после чего каждый поток указывал id от 0-n, тогда поток мог безопасно писать в своем расположении в массиве. Затем вы можете просто суммировать массив, чтобы получить общую сумму. Однако это полезно только при условии, что вам не нужно суммировать массив до того, как все потоки будут мертвы.

Чтобы быть еще более эффективным, у нас был локальный счетчик в каждом потоке, который мы тогда до того, как поток умер, приложенный к массиву.

example (псевдо-код:)

counter[nThreads];

thread(int id)
{
    // Do stuff
    if(something happened)
       counter[id]++;   
}

или

counter[nThreads];

thread(int id)
{
    int localcounter = 0;
    //Do stuff
    if(something happened)
       localcounter++;   

    //Thread is about to die
    counter[id] = localcounter;
}

Ответ 3

Вы можете использовать функцию InterlockedIncrement.

Многие функции для изменения переменных по-атомным образом документируются в MSDN под значком Функции синхронизации - они могут быть вам полезны.

Ответ 4

это не просто условие гонки, вы можете не передавать фактическое значение i вообще между потоками, если ваш компилятор решит это.

очевидно atomic - хороший способ. mutex также является хорошим способом, когда у вас нет столкновений, они так же быстро, как атомистика. Они становятся медленнее только тогда, когда им действительно нужно сделать скрипт ядра с очередями потоков sleeping и ready. То, что может получить такси, - это если сигнал ожидания не использует condition variable, и в этом случае вам может потребоваться, чтобы ядро ​​расписания для ваших потоков ready было running, что может быть очень длинным (30 мс).

Atomics обеспечит вам оптимальность, и даже может быть проще поддерживать, чем condition variable, не заботясь о событиях spurious и notify_one по сравнению с notify_all и т.д.

Если вы проверите базовые классы STL shared_ptr, созданные на С++ 11, они содержат base_count или (base_shared_count или что-то еще), которые работают точно так, как вам нужно. Вы также можете проверить новую реализацию boost:: shared_count, если хотите.