Reputation: 806
class Locker{
mutex &m_mtx;
public:
Locker(mutex& mtx) : m_mtx{mtx}{m_mtx.lock();}
~Locker(){m_mtx.unlock();}
};
mutex mtx;
int globalOutput = 0;
void sum(const vector<int>& vect, int start, int end)
{
for(auto i = vect.begin() + start; i != vect.begin() + end; ++i)
{
Locker{mtx};
globalOutput+= *i;
}
}
int main()
{
const vector<int> vct(500, 2);
thread th1(&sum, vct, 0, 250);
thread th2(&sum, vct, 250, 500);
th1.join();
th2.join();
std::cout << globalOutput;
}
I used a custom mutex
class to synchronize two threads that calculate the sum of 500 items having the value 2.
Do you know of another way to sync threads? Share knowledge, thank you!
Upvotes: 3
Views: 651
Reputation: 11516
If the goal is to calculate the sum in parallel, but you don't actually care how it happens, you can also use std::reduce
. Note that this requires C++17.
Using that your complete example code becomes:
int main () {
const vector<int> vct(500, 2);
auto sum = std::reduce(std::execution::par_unseq, vct.begin(), vct.end());
std::cout << sum;
}
Upvotes: 2
Reputation: 24738
The statement Locker{mtx};
in:
{
Locker{mtx};
globalOutput+= *i;
}
creates an object Locker
which locks the mutex mtx
, but this object is destroyed right away and the mutex is unlocked. Therefore, globalOutput
is modified by the thread while not holding a lock on the mutex. You may want instead:
{
Locker locker{mtx};
globalOutput+= *i;
// locker object destroyed here
}
That is, you want to keep the lock on the mutex while modifying globalOutput
.
std::lock_guard
or std::scoped_lock
You can just use std::lock_guard
instead of your custom Locker
class:
{
std::lock_guard<std::mutex> lck(mtx);
globalOutput+= *i;
}
Since C++17 you can simply use std::scoped_lock
, which renders std::lock_guard
obsolete:
{
// take advantage of C++17 template argument deduction for constructors
std::scoped_lock lck(mtx);
globalOutput+= *i;
}
Upvotes: 4