In a multi-threaded environment, we always have to use locks. So a common scenario is:
1 classObjectwithlock2 {3 Private:4 Std::mutex mtx_;5 Somerestype Shared_res_;6 7 Public:8 //Constructor/destructor9 ... ..Ten One voidoponsharedres () A { -Std::lock_guard<std::mutex>Lock(mtx_); - the //Read/write shared Resource:shared_res_ - ... .. - } -};Problem
How to implement copy constructor and move constructor in multi-threaded environment?
There is no challenge to implement the default constructor. Using the member initialization list, you can:
Objectwithlock (): Mtx_ (), Shared_res_ () {}//If Somerestype has a default constructor.
So how do we write the copy structure?
Programme I
A solution that is very likely to be seen is:
1 Const & RHS)2{3 lock(rhs.mtx_); 4 5 Shared_res_ = Rhs.shared_res_; 6 }
Advantages
It is clear that this solution solves our problem.
Disadvantages
The shortcomings of programme one are also obvious. is Shared_res_ = Rhs.shared_res_. Why do you say that?
Let's review the initialization order of member variables.
Initialization order of C + + member variables
The initialization order of member variables is not good in C + +, it is a variety of pits.
First, it is easy to know that the initialization of C + + member variables occurs before the constructor.
So what is the order of initialization between member variables?
There are no children's shoes dedicated to this issue, and it is likely that the order in which member variables are initialized is determined by the order of the member variable initialization list. In fact, the order in which member variables are initialized is determined by the order in which they are declared.
Question of programme I
So the problem with scenario one is that Shared_res_ = Rhs.shared_res_, it's about assignment instead of initialization. If we are going to look at this problem over and over, then the conclusion is that it will lead to performance loss (depending on the situation, it is possible to have a significant impact on performance).
Of course there are times when we don't need to be picky. In fact, the biggest problem with the scheme is that he has many limitations. These limitations require that somerestype must:
- With or without the default construction of the parameter
- Supports assignment operations
- No const member variable, no member variable of reference type, etc.
If Somerestype does not support these prerequisites, then in order to solve the current problem, either let somerestype support, or redesign the objectwithlock. If Objectwithlock is a correct design idea, then it is only difficult to somerestype. The egg hurts.
Is there no solution to this problem? Of course not, but it's not easy to do it according to C++98/03 's standards. Let's take a look at what c++11 can bring to our surprise.
Programme II
C++11 introduces a constructor-related syntax, delegate constructor, a proxy constructor. A grammar that existed early in C #, and finally in C + +.
The so-called delegate constructor, that is, the initialization of the constructor can delegate (delegates to) the class to the other constructor to complete. A simple example (from C++11 standard document 12.6.2, paragraph 6) is:
1 struct C 2 {3 C (int c) {} // #1: non-delegating constructor4 C (42 ) {} // #2: Delegate to #15 };
With the proxy constructor, our problem is easy to solve. The key to the problem we are trying to solve is to lock the RHS object before initializing the member variable.
Then the original copy constructor to do something similar to the following pseudo-code:
Const & RHS) lock(RHS) {}
With the proxy constructor, we first implement a custom constructor, be able to copy, and accept a lock:
1 Const & RHS,2 const &)3 : Shared _res_ (Rhs.shared_res_)4 {}
And then it's natural for me to have:
Const & RHS) objctwithlock (RHS, Std::lock_guard<std::mutex>(rhs.mtx_)) {}
The problem is solved perfectly!
C++98/03 Solutions
Can c++98/03 solve this problem? Yes! Of course I can! Just to see if you can think of it.
Here I will directly give the results, left to everyone to think about it.
Const & RHS) : Shared_res_ ((Std::lock_guard<std::mutex>(rhs.mtx_), rhs.shared_ Res_)) {}
Conclusion
Here's just a list of copy constructors, moving constructors like this, leave it to everyone. Finish!