I would like to use Queuing Mutex in order to control the processing of some line of codes. On the top of code file, I defined queuing_mutex as CurrentMutexType like this:
typedef queuing_mutex CurrentMutexType;
- Can I instance from CurrentMutexType inside concurrent class built up to be used by parallel_for? And which type of modifier should I use (private mutable, public static, etc.)
- After I constructed lock and acquired lock on mutex using CurrentMutexType::scoped_lock(currentMutex) (I assume that currentMutex is an instance of CurrentMutexType), how do I destroy it inside operator function in concurrent class same as concurrent class in question 1, because I cannot use CurrentMutexType::scoped_lock::release() in non-static member function?
Hello,
it is good that you typedef'ed tbb::queuing_mutex as the CurrentMutexType. Queuing mutex preserves an order in which threads asked to acquire access and if your application changes over time and you will not need to preserve this order, you can just change the typedef ot tbb::spin_mutex and the rest of the code will just work.
To answer your questions:
To implement guarded access to a section of code using TBB one should create per-thread lock objects which in turn reference a global mutex object. One way to implement it would be to have a mutex as a static member and in your class operator(), when a thread needs to acquire a lock on a mutex, you create a lock object. Lock object usually is an auto variable and mutex is acquired in it's constructor and released in the destructor, when the lock object goes out of scope (hence the name for the sub-type "scoped_lock").
The code bellow should give an idea how this can be implemented. An access to std::cout is guarded in this example, because it is not safe to write to the standard output from parallel threads. An important note here is allthough it is a good example of HOW to use synchronization primitives and on the outside it looks like a parallel application, which uses TBB, in reality this is a serial application (since all the code inside operator() of the function object is inside a critical section) + TBB scheduling overhead + synchronization overhead. This note is to remind you that if revising your algorithm is possible, so that the implementation will not have to use mutexes, it will be much more beneficial to change the algorithm first.
#include <iostream>
using namespace std;
#include "tbb/task_scheduler_init.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_sort.h"
#include "tbb/queuing_mutex.h"
typedef tbb::queuing_mutex currentMutex_t;
class A {
public:
static currentMutex_t mutex;
void operator() (const tbb::blocked_range<size_t>& r) const {
for(size_t i = r.begin(); i < r.end(); ++i) {
currentMutex_t::scoped_lock lock(mutex);
cout << i << " ";
}
}
};
currentMutex_t A::mutex = currentMutex_t();
int main(int argc, char* argv[]) {
tbb::task_scheduler_init init;
tbb::parallel_for(tbb::blocked_range<size_t>(0, 20), A(), tbb::auto_partitioner());
return 0;
}