C + + Multithreading learning---shared data between threads

Source: Internet
Author: User
Tags mutex

Shared data between multithreading is problematic if it is not constrained. The easiest way to do this is to use some kind of protection for the data structure, and the popular expression is:

Make sure that only the thread that makes the modifications can see the middle state when the invariant is corrupted. From the point of view of other access threads, the modification is either done or not started.

1. Protect shared data with mutexes

When accessing the shared data, lock the data with the mutex, and then unlock the data when the access is complete. The C + + standard library provides a RAII syntax template class Std::lack_guard for mutexes that provide locked mutexes at the time of construction and unlock them at the time of the destructor.

#include <list>
#include <mutex>
#include <algorithm>
std::list<int> some_list ; 1
Std::mutex Some_mutex//2
void add_to_list (int new_value)
{
    std::lock_guard<std::mutex> Guard (Some_mutex); 3
    some_list.push_back (new_value);
}
BOOL List_contains (int value_to_find)
{
    std::lock_guard<std::mutex> Guard (Some_mutex);//4
    Return Std::find (Some_list.begin (), Some_list.end (), Value_to_find)!= some_list.end ();
The above example uses lock_guart<std::mutex> to make the two functions of add_to_list and list_contains mutually exclusive access to the data. Thus ensuring the security of data in multiple threads.


2. The internal conditions of the Embankment interface competition

For example, you are writing a stack of data structure, and the stack of push\pop\top\empty\size\ and other interfaces to increase mutual exclusion protection. Look at the following code:

stack<int> s;
if (! S.empty ()) {//1
int Const VALUE = s.top ();//2
s.pop ();//3
do_something (value);
}
This is just single-threaded security, and for multithreading between 1 and 2, there may be a pop () call from another thread and delete the last element, which can cause problems.

Because the size of the lock is too small, the operation that needs to be protected is not covered entirely. You may consider increasing the size of the lock appropriately.

Std::shared_ptr<t> pop ()
{
    std::lock_guard<std::mutex> lock (m);
    if (Data.empty ()) throw Empty_stack (); Before calling POPs, check to see if the stack is empty
    std::shared_ptr<t> const res (std::make_shared<t> (Data.top ()));//Assign return value before modifying stack
    Data.pop ();
return res;
}
void Pop (t& value)
{
    std::lock_guard<std::mutex> lock (m);
    if (Data.empty ()) throw Empty_stack ();
    Value=data.top ();
    Data.pop ();
}

3. Dike deadlock problem

The biggest problem with deadlocks is that you lock an operation by two or more than two mutexes. A pair of threads needs to do something with all their mutexes, each of which has a mutex and waits for another unlock. So no threads can work because they are waiting for each other to release the mutex.

The general recommendation to avoid deadlocks is to have two mutexes always locked in the same order: always lock the mutex a before the mutex B, which can effectively prevent most problems. A better approach is to use the Std::lock provided by C + + to lock multiple mutexes at once. See the following example:

Class Some_big_object;
void swap (some_big_object& lhs,some_big_object& RHS);
Class X
{
private:
    some_big_object some_detail;
    Std::mutex m;
Public:
    X (some_big_object const& SD): Some_detail (SD) {}
    friend void swap (x& lhs, x& rhs)
    {
        if (&LHS==&RHS) return
    	  ;
        Std::lock (LHS.M,RHS.M); 1
        std::lock_guard<std::mutex> lock_a (lhs.m,std::adopt_lock);//2
        Std::lock_guard<std::mutex > Lock_b (rhs.m,std::adopt_lock); 3
        Swap (lhs.some_detail,rhs.some_detail);
     }
;
The call to Std::lock () ① locks two mutexes, and two Std:lock_guard instances have created a ②③ and a mutex. Provides the Std::adopt_lock parameter, in addition to indicating that the Std::lock_guard object is locked, also represents a ready-made lock, rather than attempting to create a new lock.

Note: A mutex can be locked multiple times on the same thread (Std::recursive_mutex).

Here are a few tips for designing locks:

1. Try to avoid nested locks, one thread has acquired a lock, do not get a second

2. Avoid the holding of locks that are invoked by user-supplied code because the user's code is unpredictable and there may be a deadlock occurring

3. Acquire a lock in a fixed order-when more than one lock is used to avoid deadlocks effectively

For the problem of the granularity of the lock also need attention, large granularity can protect more data, but its impact on performance is greater. At the same time, as much as possible, he minimizes the holding time of locks.

The comparison operator locks one mutex at a time:

friend bool operator== (y const& lhs, y const& rhs)
{
    if (&LHS==&RHS) return
        true;
    int const lhs_value=lhs.get_detail (); 2
    int const rhs_value=rhs.get_detail ();//3 return
    Lhs_value==rhs_value;//4
}


Using a copy of int to reduce the latency of locks is an efficient approach.

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.