TBB spin_mutex внутри parallel_for, чтобы заблокировать критическую секцию

Концептуально, как я могу заблокировать критический раздел внутри tbb::parallel_for звоните используя tbb:spin_mutex? Критический раздел составляет менее 20 инструкций, поэтому spin_mutex идеально. Например, следующий фиктивный код объясняет ситуацию:

function() {
    // I'm using lambda functions in parallel_for call here. The parallel_for 
    // is multithreading across the size of the vector customVec
    tbb::parallel_for(
        tbb::blocked_range<vector<CustomeType>::iterator>(customVec.begin(), customVec.end(), 1), 
        [&](tbb::blocked_range<vector<CustomType>::iterator> customVec) {
            for (vector<CustomType>::iterator it = customVec.begin(); it != customVec.end(); it++) {
                CustomType item = *it;
                ...
                ...

                // This is the cross-functional call that each thread will call
                // critical section is in-side the functionA
                item->functionA(param1, param2);
            }

            ...
            ...   
        }
    );

    ...
    ...
}

И функция А:

functionA (Type1 param1, Type2 param2) {
    if (conditionX) {
        /* This file read is the critical section. So basically, need to 
        block multiple threads reading the same file to reduce I/O cost
        and redundancy. Since file read can be stored in a global variable
        that can be accessed in memory by other threads */

        fileRead(filename); // Critical line that need to be protected
    }
    ...
    ...
}

То, с чем я борюсь, это то, как я могу настроить spin_mutex в functionA() таким образом mutex распределяется между потоками, и потоки не переступают друг друга, пытаясь одновременно выполнить критический раздел.

Примечание: предположим, function() а также functionA() принадлежит двум отдельным классам C++, и нет никакого наследования на основе классов между двумя классами, которые имеют function() а также functionA() как функции-члены

2 ответа

Вы можете рассмотреть возможность использования статического spin_mutex внутри функции:

functionA (Type1 param1, Type2 param2) {
    if (conditionX) {
        /* This file read is the critical section. So basically, need to 
        block multiple threads reading the same file to reduce I/O cost
        and redundancy. Since file read can be stored in a global variable
        that can be accessed in memory by other threads */

        // A static mutex that is shared across all invocations of the function.
        static tbb::spin_mutex mtx;
        // Acquire a lock 
        tbb::spin_mutex::scoped_lock lock(mtx);
        fileRead(filename); // Critical line that need to be protected
    }
    ...
    ...
}

Обратите внимание, что он будет работать только с C++11 и более поздними версиями (потому что вам нужна "магическая статика", то есть потокобезопасность инициализации статической переменной).

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

В основном решение состоит в том, чтобы определить spin_mutex объект за пределами parallel_for и передать его в вызов функции в качестве ссылки. Я разместил тот же пример кода из вопроса с решением ниже:

tbb::spin_mutex mtx;   // Declare spin_mutex object
function() {
    // I'm using lambda functions in parallel_for call here. The parallel_for 
    // is multithreading across the size of the vector customVec
    tbb::parallel_for(
        tbb::blocked_range<vector<CustomeType>::iterator>(customVec.begin(), customVec.end(), 1), 
        [&](tbb::blocked_range<vector<CustomType>::iterator> customVec) {
            for (vector<CustomType>::iterator it = customVec.begin(); it != customVec.end(); it++) {
                CustomType item = *it;
                ...
                ...

                // This is the cross-functional call that each thread will call
                // critical section is in-side the functionA
                item->functionA(param1, param2, mtx);   // pass object as a reference
            }

            ...
            ...   
        }
    );

    ...
    ...
}

и функция А:

// Pass the spin_mutex object by reference
functionA (Type1 param1, Type2 param2, tbb::spin_mutex& mtx) {
    if (conditionX) {
        /* This file read is the critical section. So basically, need to 
        block multiple threads reading the same file to reduce I/O cost
        and redundancy. Since file read can be stored in a global variable
        that can be accessed in memory by other threads */

        // Acquire a scope lock 
        { 
            tbb::spin_mutex::scoped_lock lock(mtx);
            fileRead(filename); // Critical line that need to be protected
        }
    }
    ...
    ...
}
Другие вопросы по тегам