A detailed description of the role of volatile in C + +

Source: Internet
Author: User

Introduction to Volatile

Volatile is similar to what is known as a const is also a type modifier. Volatile is an indication to the compiler that the object it is modifying should not perform optimizations. The role of volatile is to be used for multithreaded programming. In a single thread, that's the only way to limit compiler optimizations. So the single-threaded kids ' shoes don't have to waste their energy looking at the bottom.

No result of volatile

Without volatile, you will not be able to use the basic variable in parallel in multiple threads. Here is an example of my development project (this example uses the C # language but does not prevent us from discussing C + +). One in the school. NET project, I used to use a basic variable Int32 type in multithreaded monitoring, which I used to control a condition of monitoring in multi-threading. Considering that the basic variables are the compiler comes with and cannot be locked with lock, I take it for granted that the atomic operation will not have a multi-threading problem, can actually run after the discovery program run sometimes normal and sometimes abnormal, instead of using Dictionary object processing and lock after the completely normal. Now want to be multi-threaded operation of the variable at the same time, the specific will be clear below.

The role of volatile

If a basic variable is modified by volatile, the compiler will not save it to the register, but each time it accesses the location in memory where the variable is actually saved. This avoids the catastrophic problem caused by compiler optimizations in the multi-threaded read and write of variables without volatile modifiers. Therefore, you must add the volatile modifier to the basic variables that you have to share in multiple threads. Of course, volatile also allows you to capture non-thread-safe code at compile time. I would also like to introduce a way for Daniel to use smart pointers to sequence shared area code.

It is difficult to write code that is unusually secure in generic programming, but it is too small for pediatrics to be difficult for multithreaded programming. Multithreaded programming you need to prove that it is correct, you need to repeatedly dry debugging and repair, of course, the resource competition is also necessary to pay attention to, the most hateful is, sometimes the compiler will give you some color to see ...

class Student {public:void wait ()  //Waiting for dinner in the Beihang University is a very painful thing ... {while (!flag) {Sleep (); //Sleeps for milliseconds}} void Eat () {flag = true;} ... private:bool flag; };

Well, in multi-threading you just wait to eat, but in this place you can never wait, because flag is put into the register by the compiler, even if the child in front of you to tell you flag=true, but you are blind to see these. This weird situation occurs because the values you use are saved in the register before, so the flag value on the original address changes and you don't get it. What should we do? By the right, the change to volatile is solved.

The use of volatile for basic types and user-defined types differs from const, for example, you can assign a basic type of non-volatile to volatile, but you cannot assign the non-volatile of a user-defined type to volatile. And a const is all that is possible. Another difference is that the compiler auto-synthesized copy control does not apply to volatile objects because the synthetic copy control members receive const parameters, which are const references to class types, but cannot pass volatile objects to ordinary or const references.

How to use good volatile in multiple threads

In multi-threading, we can use the mechanism of locking to protect the resource critical area. The operation of shared variables outside the critical section requires volatile and is non-volatile in the critical section. We need a tool class lockingptr to preserve the acquisition of mutexes and the use of volatile const_cast transformations (through const_cast for volatile conversions).

First we declare a framework for the mutex class to be used in a lockingptr:

class Mutex {public:void acquire (); void Release ();..};

Then declare the most important Lockingptr template class:

template <typename t> class Lockingptr {public:  //constructors/destructorslockingptr (volatile T & obj, mutex& mtx): Pobj_ (const_cast<t*> (&obj)), Pmtx_ (&MTX) {MTX. Lock (); } ~lockingptr () {Pmtx_->unlock ();} //Pointer behavior t& operator* () {return *pobj_;} t* operator-> () {return pobj_;} private:t* pobj_; mutex* pmtx_; Lockingptr (const lockingptr&); lockingptr& operator= (const lockingptr&); };

Although this class looks simple, it is very useful in writing multi-threaded programs that you strive for. You can use it to make operations on objects shared in multiple threads as simple as the volatile modifier's basic variables and never used to const_cast. Here's an example:

Suppose there are two threads that share a Vector<char> object:

class Syncbuf {public:void Thread1 (); void Thread2 (); private:typedef vector<char> buft; volatile buft buffer _; Mutex mtx_; //Controls access to Buffer_};

In the function Thread1, you control access to BUFFER_ member variables through lockingptr<buft>:

void Syncbuf::thread1 () {lockingptr<buft> lpbuf (buffer_, mtx_); Buft::iterator i = Lpbuf->begin (); for (; I! = Lpbuf->end (); ++i) {... use *i ...}}

This code is easy to write and understand. As long as you need to use buffer_ you have to create a lockingptr<buft> pointer to point to it, and once you do, you get the entire interface of the container vector. And once you make a mistake, the compiler will point it out:

void Syncbuf::thread2 () {  //error! Cannot access ' begin ' for a volatile objectbuft::iterator i = Buffer_.begin ();  //error! Cannot access ' end ' for a volatile object for (; I! = Lpbuf->end (); ++i) {... use *i ...}}

In this case, you only have access to the member functions and variables through const_cast or lockingptr. The difference between the two methods is that the latter provides a sequential approach to the implementation while the former is achieved by converting to volatile. Lockingptr is pretty well understood, if you need to call a function, you create an unnamed temporary Lockingptr object and use it directly:

unsigned int syncbuf::size () {return lockingptr<buft> (Buffer_, Mtx_)->size ();}
The use of lockingptr in basic types

In the above we describe the use of volatile to protect objects from accidental access and use LOCKINGPTR to provide simple and efficient multithreaded code. Now let's talk about one of the more common types of multithreading shared primitives:

class Counter {public: ... void Increment () {++ctr_;} void Decrement () {--ctr_;} Private:int ctr_; };

At this time, it may be possible for everyone to see where the problem lies. 1.ctr_ need to be volatile type. 2. Even ++ctr_ or--ctr_, this still requires three atomic operations in processing (read-modify-write). Based on the two points above, this class can have problems in multiple threads. Now we're going to use lockingptr to solve:

class Counter {public: ... void Increment () {++*lockingptr<int> (ctr_, mtx_);} voiddecrement () {-?*lockingptr <int> (Ctr_, mtx_); } private:volatile int ctr_; Mutex mtx_; };

Volatile member functions

In the case of classes, first, if the class is volatile, the members inside are volatile. The second thing to declare a member function as volatile is to declare it at the end of the function as a const. When you design a class, the volatile member functions you declare are thread-safe, so functions that may be called at any time should be declared volatile. Consider that volatile equals thread-safe code and non-critical areas; Non-volatile equals single-threaded scenes and is in the critical section. We can use this volatile overload of a function to make a trade-off between thread security and speed first. The specific implementation is omitted here.

Summarize

Key four points for using volatile in writing multithreaded programs:

1. Declare all shared objects to be volatile;

Related Article

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.