Is this the correct use of force state variables?

I wrote the following program for alternatively incrementing and doubling a counter (incrementing the first time) using formatting state variables. Can anyone tell me if this is the correct use of forcing state variables. It is working correctly. I do not understand how to use the lock function call while waiting. What does condition.wait (lock) mean? For example, what is the use of two brackets in increments and multiplication in this program. How can I avoid them?

#include <boost/thread/thread.hpp> #include <boost/thread/mutex.hpp> #include <boost/bind.hpp> #include <boost/thread/locks.hpp> #include <boost/thread/condition_variable.hpp> #include <iostream> #include <stdlib.h> #include <time.h> using namespace std; int counter=0; boost::mutex m1,m2; bool incremented=false,multiplied=false; boost::condition_variable c1,c2; void Increment() { { boost::mutex::scoped_lock lk(m1); counter++; incremented = true; c1.notify_one(); while(!multiplied) c2.wait(lk); multiplied=false; } } void Multiply() { { boost::mutex::scoped_lock lk(m2); while(!incremented) c1.wait(lk); incremented = false; counter = counter*2 ; multiplied = true; c2.notify_one(); } } void IncrementNtimes(int n){ for(int i=0;i<n;i++){ Increment(); } } void MultiplyNtimes(int n){ for(int i=0;i<n;i++){ Multiply(); } } int main(int argc, char* argv[]) { srand ( time(NULL) ); boost::thread thrd1(boost::bind(&IncrementNtimes,20)); boost::thread thrd2(boost::bind(&MultiplyNtimes,20)); thrd1.join(); thrd2.join(); cout<<"Main counter is:"<<counter<<endl; return 0; } 
+4
source share
3 answers

No, this is not true. You are almost there, but the big problem is that the Multiply and Increment functions should use the same mutex.

A mutex is an object that provides MUTual EXclusion. In other words, the point of a mutex is to prevent two threads from simultaneously touching the same variable and getting unpredictable results. A mutex is like a token that has one stream at a time, which gives it the right to access a specific variable (or set of variables). In this case, the variable you are trying to protect is counter . There should be one and only one mutex that controls access to counter . In your case, each stream will contain its own token, which, in its opinion, gives it the right to access the counter, and therefore there will be unpredictable behavior.

You hold the mutex by blocking it. This is the point of locks, and why you cannot "escape" them. The whole point of locked locks is that provided that you only have one mutex m , when one of the threads holds the lock on m , the other thread is also guaranteed not to hold the lock on m . If you encoded correctly, committing to m should be a prerequisite for accessing counter , so the value of counter should be predictable.

Now, in relation to wait() . Calling wait() means "I refuse to lock this mutex until someone else signals this state, and then I want to return it." At the same time, the flow stops. Suppose you have only one mutex m and condition c , and lk is a lock on m , the line c.wait(lk) means that the thread will refuse to lock lk on m and then pause execution until some other thread will not call c.notify_one() (or c.notify_all() ). When the waiting thread returns from the call to wait() , it will automatically restart the lk lock on m , and therefore it is allowed to access counter again.

Finally, these locks are “copied” locks. This means that they are automatically released upon destruction (when they go beyond). Thus, in this case, each function retains its lock until it exits, unless it abandoned its lock to wait and paused while waiting for a signal.

+18
source

Locks are tied to condition variables, because condition variables have no state - if stream A signals when there are no waiters and stream B then goes to wait, B will not be woken up. For this reason, there must be a state that is also tied to a condition variable (in this case it increases and multiplies). A lock protects this state, which is accessed through multiple threads. When you go through the lock wait (), wait when you release the lock atomically and wait on the condition variable, and reset the lock when the wait returns. This means that there is no window in which the state of the state variable could change and wait.

For example, if a condition variable is not bound to a lock:

 // In thread A while(!incremented) // Context switch to Thread B: incremented = true; // Context switch to Thread A:) c1.wait(); // Whoops, waiting for a condition that already happened. 
+4
source

A condition variable should always be associated with a lock. The bottom line is that you must hold the lock to call wait (), but once inside wait () the lock is released. When you signal, you must also hold the lock, and finally, the wait will not return (even after the signal) until the signaling device releases the lock.

+2
source

Source: https://habr.com/ru/post/1299772/


All Articles