Data protection in C + + multithreaded programming _c language

Source: Internet
Author: User
Tags mutex thread class

When writing multithreaded programs, multiple threads accessing a shared resource at the same time can cause synchronization problems, and in this article we will cover data protection in c++11 multithreaded programming.
Data Loss

Let's start with a simple example, see the following code:

 #include <iostream> #include <string> #include <thread> #include <v
Ector> using Std::thread;
Using Std::vector;
Using Std::cout;
 
Using Std::endl;
 
  Class Incrementer {Private:int counter;
 
    Public:incrementer (): counter{0} {};
      void operator () () {for (int i = 0; i < 100000; i++) {this->counter++;
    } int Getcounter () const {return this->counter;
 
}   
};
 
  int main () {//Create the threads which'll each do some counting vector<thread> threads;
 
  Incrementer counter;
  Threads.push_back (Thread (std::ref (counter)));
  Threads.push_back (Thread (std::ref (counter)));
 
  Threads.push_back (Thread (std::ref (counter)));
  for (auto &t:threads) {t.join ();
 
  } cout << Counter.getcounter () << Endl;
return 0; }

The purpose of this program is to count, count to 300,000, some silly fork programmers want to optimize the process of counting, so create three threads, use a shared variable counter, each thread is responsible for adding 100,000 count to this variable.

This code creates a class named Incrementer that contains a private variable counter, whose constructor is very simple, but sets the counter to 0.

This is followed by an operator overload, which means that each instance of the class is invoked as a simple function. Generally we call a method of a class this way Object.foomethod (), but now you actually call the object directly, like object (). Because we are passing the entire object to the thread class in the operator overload function. Finally, a Getcounter method that returns the value of the counter variable.

Again, the entry function of the program, Main (), we created three threads, but we created only one instance of the Incrementer class, then passed the instance to three threads, noting that the std::ref is used, which is equivalent to passing the reference object of the instance, not the copy of the object.

Now let's take a look at the results of the program execution, and if the idiot programmer is smart enough, he will use GCC 4.7 or later, or clang 3.1来 to compile and compile the method:

g++-std=c++11-lpthread-o threading_example main.cpp

Run Result:


[Lucas@lucas-desktop src]$./threading_example
218141
[lucas@lucas-desktop src]$./threading_example
208079
[lucas@lucas-desktop src]$./threading_example
100000
[Lucas@lucas-desktop src]$./threading_ Example
202426
[lucas@lucas-desktop src]$./threading_example
172209

But wait, no, the program does not count to 300,000, once only to the count of 100,000, why is this so? Okay, plus 1. The actual processor instructions actually include:

MOVL  Counter (%rip),%eax
addl  $,%eax movl%eax  , counter (%rip)

The first instruction will load the value of the counter to the%eax register, followed by a register value of 1, and then move the value of the register to the address of the counter in memory.

I hear you muttering: That's good, but why do you have to count the wrong questions? Well, remember we said before that threads share the processor because there is only one core. So at some point, a thread executes according to an instruction, but in many cases the operating system says to the thread, "time is over, come back in line, and then another thread starts executing, and when the next thread starts executing, it starts at the point where it is paused." So you guess what happens when the current thread is preparing to perform a register plus 1 operation, the system will give the processor to another thread?

I really don't know what's going to happen, maybe we're preparing to add 1 o'clock, another thread comes in, and counter the value to the register. No one knows what's going on.

The right approach

The solution is to require that only one thread is allowed to access the shared variable at the same time. This can be solved by Std::mutex class. When the thread enters, lock, performs the action, and then releases the lock. Other threads that want to access the shared resource must wait for the lock to be released.

Mutex (mutex) is an operating system that ensures that lock and unlock operations are indivisible. This means that the thread is not interrupted in the process of locking and unlocking the mutex. When a thread locks or unlocks a mutex, the operation completes before the operating system switches the thread.

And the best thing is that when you try to lock the mutex, the other thread has locked the mutex, and you have to wait until it is released. The operating system tracks which threads are waiting for which mutex, and the blocked thread enters the "blocked onm" state, meaning that the operating system does not give the blocked thread any processor time until the mutex is unlocked and therefore does not waste the CPU cycle. If more than one thread is in the waiting state, which thread first gets the resource depends on the operating system itself, typically Windows and Linux systems use FIFO policies and are prioritized in real-time operating systems.

Now let's improve on the code above:

#include <iostream> #include <string> #include <thread> #include <vector> #include <mutex
> Using Std::thread;
Using Std::vector;
Using Std::cout;
Using Std::endl;
 
Using Std::mutex;
    Class Incrementer {Private:int counter;
 
  Mutex m;
 
    Public:incrementer (): counter{0} {};
        void operator () () {for (int i = 0; i < 100000; i++) {This->m.lock ();
        this->counter++;
      This->m.unlock ();
    } int Getcounter () const {return this->counter;
 
} 
};
 
  int main () {//Create the threads which'll each do some counting vector<thread> threads;
 
  Incrementer counter;
  Threads.push_back (Thread (std::ref (counter)));
  Threads.push_back (Thread (std::ref (counter)));
 
  Threads.push_back (Thread (std::ref (counter)));
  for (auto &t:threads) {t.join ();
 
  } cout << Counter.getcounter () << Endl;
return 0;
 }

Notice the changes on the code: we introduced the mutex header file, added a member of M, the type is a mutex, in operator () we lock the mutex m and then add 1 to the counter and then release the mutex.


Again, the results of the above procedures are as follows:

[Lucas@lucas-desktop src]$./threading_example
300000
[lucas@lucas-desktop src]$./threading_example
300000

That's the right number. But in computer science, there is no free lunch, and using mutexes lowers the performance of the program, but it's better than a bad program.

Prevent exceptions

When you add 1 to a variable, it is possible that an exception can occur, and of course there is little chance of an exception in our case, but it is highly probable in some complex systems. The above code is not exceptionally safe, when the exception occurs, the program has ended, but the mutex is still in the state of the lock.

To ensure that the mutex can be unlocked in the event of an exception, we need to use the following code:

for (int i = 0; i < 100000 i++)
{
 this->m.lock ();
 Try
  {
   this->counter++;
   This->m.unlock ();
  }
  catch (...)
  {
   this->m.unlock ();
   Throw
  }
}

However, this code is too much, but only to lock and unlock the mutex. It's okay, I know you're lazy, so it's a simpler single line code solution that uses the Std::lock_guard class. This class locks the mutex object when it is created, and then releases it at the end.

To continue modifying the code:

void operator () ()
{for
  (int i = 0; i < 100000; i++)
  {
  lock_guard<mutex> lock (this->m);
 
  The lock has been created now, and immediatly locks the mutex
  this->counter++;
 
  This is the For-loop scope, and the lock would be
  //destroyed, and in the destructor of the lock, it wil L
  //Unlock the Mutex
  }

The code above is already exceptionally secure, because when an exception occurs, the destructor of the lock object is called, and the mutex is automatically unlocked.

Remember, use the drop code template to write:


void Long_function ()
{
  //Some long code
 
  //Just a pair of curly braces
  {
  //Temp scope, create lock
   lock_guard<mutex> Lock (this->m);
 
  Do some stuff
 
  //Close the scope, so the guard would unlock the mutex
  }

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.