See an article that is not bad, the specific location of the article can not be found, copy it, keep the review in the future.
This micro-blog has triggered a lot of discussion of friends: the approver has it; the critics have it; of course, more friends, I hope I can read the C/C++ Volatile key words in more detail to testify my view. This is exactly what I wrote this blog: This article will analyze C in detail.The functions of the /C++ Volatile keyword (many functions), the problems of the Volatile keyword in multithreaded programming, the relationship between the Volatile keyword and the compiler /CPU, the C/C++ Volatile and Java VolatThe difference between ile and the origin of Volatile keyword is hoped to help you better understand and use C/C++ Volatile.
Volatile，The explanation in the dictionary is: easy to lose; changeable; volatile. The C/C++ variables modified with this keyword should also reflect the “changeable” characteristics. Most people recognize Volatile, which is also based on this feature, and this is also the C/C++ Vola of this article.The first feature of tile.
Before introducing the “mutability” of C/C++ Volatile keywords, let’s take a look at the following two code fragments, and their corresponding assembly instructions (the assembly code for the following use cases, all of the Release versions compiled by VS 2008):
Test case 1: non Volatile variables
b = a + 1;This statement corresponds to the assembly instructions: lea ECX, [eax + 1]. Due to the variable a, when the previous statement a = FN (c) is executed, it is cached in the register eax, so B = a + 1; the statement, which can be used directly in register EAThe A in X is calculated, corresponding to the assemblage: [eax + 1].
Test case two: Volatile variable
The only difference between the test case and the test case is that the variable a is set to the volatile attribute. A small change brings about great changes in the assembly code. A = FN (c) after execution, a in register ECX is written back to memory: mov DWORD PTR [Esp+0Ch], ECX. Then, in the execution of B = a + 1, the variable a is re read from memory: mov eax, DWORD PTR [esp + 0Ch], and no longer directly using the contents of register ECX.
From the above two use cases, we can see the first characteristic of C/C++ Volatile Keywords: volatility. The so-called variability, which is reflected in the assembly level, is two statements, and the next statement does not directly use the register of the corresponding volatile variable in the last statement.Capacity, instead of reading from memory. This feature of volatile is believed to be a feature that most friends understand.
After understanding the “mutable” feature of the C/C++ Volatile keyword, let’s then continue to analyze the next feature of Volatile: “non optimisation” features.
Similar to the “mutability” nature described earlier, the second features of the C/C++ Volatile keyword: “non optimisation”, also illustrated by two contrasting code fragments:
Test case three: non Volatile variables
In this use case, the non – volatile variables a, B, and C are all optimized by the compiler (optimize out), because the compiler finds that three variables of a, B, and C are useless and can be replaced by constant. The final assembly code is fairly brief and efficient.
Test case four: Volatile variable
Test case four is similar to test case three. The difference is that a, B, and C three variables are all volatile variables. This difference, reflected in the assembly language, is that three variables still exist, and that three variables need to be read into the register from memory, and then the printf is called.() function.
From test case three or four, we can summarize the second characteristics of C/C++ Volatile Keywords: “non optimisation”. Volatile tells the compiler not to radically optimize my variables, or even eliminate variables directly, so that programmers can write them in code.Instructions are bound to be executed. Relative to the first feature mentioned above: “mutability”, “not optimisation”, the characteristics may be relatively small. However, compared with the third characteristics of C/C++ Volatile mentioned below, whether it is “changeable” or “can not be optimized”.Sex is a very popular concept of Volatile keyword.
C/C++ VolatileThe two features mentioned above let Volatile often be interpreted as a key word for multithreading: a global variable that can be accessed / modified by multiple threads at the same time, then the thread within the thread can not assume the invariance of the variable, and based on this hypothesis, do some programming. Of courseIn this case, there is no problem in itself. Multithreaded programming, concurrent access / modification global variables, usually suggest adding Volatile keyword modification to prevent unnecessary optimization of the C/C++ compiler. But, many times, C/C++ VolatileKeywords, in multithreading environment, will be given more functions, resulting in problems.
Back to the background of this article, my micro-blog, my friend, has just made such a problem. Its use of C/C++ Volatile keywords can be abstracted into the following pseudo code:
This pseudo code declares another flag variable of Volatile. A thread (Thread1) modifies the variable after completing some operations. The other thread (Thread2) reads the flag variable continuously, because the flag variable is declared.The volatile attribute, so the compiler does not read the variable from the register every time when it is compiled, and does not rewrite the if (flag = = true) directly to if (false = = true) through a variety of radical optimizations. As long as flaThe G variable is modified in Thread1, which is read in Thread2. It enters if condition judgment and then enters if to process it. Within the if condition, because flag = = true, suppose somethi in Thread1.The ng operation must have been completed. On the basis of this assumption, proceed with the following other things operation.
By declaring the flag variable as a volatile attribute, it makes good use of the two features of the C/C++ Volatile mentioned earlier in this article: “mutability”; “not optimisation”. In principle, this is a good application for volatile keywords, and look at it.Friends here can check and check their own code. I believe there will be such a use.
However, this multithreaded application seems to be a big problem for the perfect application of C/C++ Volatile keyword. The key to the problem lies in the red text in front of it: suppose flag = true, suppose something in Thread1.The operation must have been completed. Flag = = true, why can we infer that the something in Thread1 must have been completed? In fact, since I regard this as a wrong use case, the answer is clear: this inference is not valid. You can not assume that you can see Fla.G = = true, flag = true; the something in front of this statement must have been executed. This leads to the third characteristics of C/C++ Volatile keyword: sequentially.
Also, to illustrate the “sequential” features of the C/C++ Volatile keyword, three simple use cases are given below (Note: unlike the test cases above, the following three use cases, based on the Linux system, using “GCC: (Debian 4.3)..2-1.1) 4.3.2 “):
Test case five: non Volatile variables
A simple example is that global variables A and B are non volatile variables. By compiling the GCC O2 optimization, you can be surprised to find that the assignment order of the two variables of A and B has been changed!!! In the corresponding assembly code, the B = 0 statement is executed first and then A.= B + 1 statement is executed.
Here, I will briefly introduce the most basic principle of C/C++ compiler optimization: to ensure that the output of a program is unchanged before and after optimization. Applying this principle to the above, we can find that although GCC optimizes the assignment order of A and B variables, the execution result of foo () function is optimized before.Nothing changed after that, still A = 1; B = 0. So it is feasible to do this.
Test case six: a Volatile variable
The difference between this test and test case five is that the variable B is declared as a volatile variable. By looking at the corresponding assembly code, B is still assigned before A, and the Volatile variable B does not prevent compiler optimization from happening, and the compiler still has a chaotic sequence.Elephants.
It seems that the operations between C/C++ Volatile variables and non Volatile variables may be in the order of the compiler’s exchange.
Through this use case, it has been well illustrated that in the front of this chapter, flag = = true is assumed to assume that something must be completed. In multithreading, the use of volatile can cause serious problems. But, this is not the end, please continueLook at the test case seven below.
Test case seven: two Volatile variables
At the same time, A, B two variables are declared volatile variables, and then look at the corresponding assembly. Miracles happen, A and B disorderly ordering. At this point, the assembly code, with the order of user code, is always assigned a variable A first, and then assigning variable B.
In this way, the operations between C/C++ Volatile variables will not be exchanged sequentially by the compiler.
Through test case six, we can conclude that the sequence of operations between C/C++ Volatile variables and non Volatile variables may be exchanged by compilers. Therefore, the pseudo code on multithread operation may become the following order in the actual operation.
Because the code execution sequence in Thread1 is changed, flag = true is advanced to something, so the hypothesis of the whole Thread2 is invalid. Because something was not implemented, Thread2 entered the if generation.Code segment, the entire multithreaded code logic problems, resulting in multithread completely wrong.
The careful reader sees here, may ask questions, according to test case seven, C/C++ Volatile variables, the compiler is able to guarantee no exchange order, then can all of the variables in something be set to volatile? This prevents the compilationThe order optimization of the translator ensures the correctness of the multithreaded program.
Unfortunately, it is still not possible to solve this problem. It is certain that all variables are set to volatile, which prevents the compiler from sequencing optimization first. But don’t forget that the compiler’s code is ultimately executed through CPU. At present, there are various kinds of marketsDifferent architecture of CPU products, CPU itself to improve the efficiency of code operation, also to the code execution order to be adjusted, this is the so-called CPU Memory Model (CPU memory model). For CPU’s memory model, you can refer to these data: MeMory Ordering From Wiki; Memory Barriers Are Like Source Control Operations Control;Che and Memory Ordering From ho Da Cheng. The following is a graph intercepted from Wiki, which lists the possible order disorder of different CPU architectures.
As you can see from the diagram, the X86 system (X86, AMD64), which is the most widely used CPU, also has the behavior of instruction disorder execution: StoreLoad disorder, which can be done before the write operation.
So, back to the above example, even if all the variables are declared to be volatile, even if the compiler’s disorderly optimization is eliminated, CPU may still execute instructions in a disorderly sequence for the generated assembler code, resulting in a logic error of the program dependency, and volatile is incapable of this.Force.
In fact, the real right way for this multithreaded application is to build a happens-before semantics. For the definition of happens-before semantics, we can refer to the article: The Happens-Before Relation.Next, we use the form of graph to show the happens-before semantics.
As shown, the so-called happens-before semantics is to guarantee all the code in the Thread1 code block, which must be done before the first code of the Thread2 code block. Of course, there are many ways to build such a semantics. Our commonly used Mutex, SpiNlock, RWLock, all can guarantee this semantics (about the construction of happens-before semantics, and why locks can guarantee happens-before semantics, and later on to write an article for discussion). But, C/C++ VolatThe Ile keyword does not guarantee this semantics, which means the C/C++ Volatile keyword, in a multithreaded environment, if the use of not careful, will produce the error as I mentioned here.
C/C++ VolatileThe third characteristic of keywords is “sequentially”, which ensures the order between Volatile variables, and the compiler does not make disorder optimization. The order of Volatile variables and non Volatile variables is that the compiler does not guarantee the order and may make disorder optimization. At the same time, C/C++ Volatile keyword, and can’t be used to build happens-before semantics, so when you are doing multithreaded programming, use volatile carefully and don’t fall into the use of volatile variables.
After introducing the key words of C/C++ Volatile, I will give a brief introduction to the Volatile of Java. Similar to C/C++’s Volatile keyword, Java’s Volatile also has these three characteristics, but the biggest difference is third special ones.Sex, “sequentially”, Volatile of Java has been greatly enhanced, and the operation of Java Volatile variables comes with the semantics of Acquire and Release. The so-called Acquire and Release semantics can refer to the article: AcquIre and Release Semantics. (this point, if necessary, we can write an article devoted to discussion). The semantics of Acquire and Release supported by Java Volatile are as follows:
1、For the write operation of the Java Volatile variable, with Release semantics, the read and write operations for any other variable before all Volatile variable writes are not optimized by the compiler and CPU, after a random sequence to the write operation of the Volatile variable.Execution.
2、For the read operation of the Java Volatile variable, with Acquire semantics, all read and write operations for any other variable after the read operation of all Volatile variables will not be optimized by the compiler and CPU before the read operation of the Volatile variable.Conduct。
Through the Acquire, Release semantics of Java Volatile and the comparison of C/C++ Volatile, it can be seen that Java Volatile is more stringent for the compiler and CPU disorder optimization. Java VoSome random operations on latile variables and non Volatile variables are also prohibited.
Because Java Volatile supports Acquire and Release semantics, Java Volatile can be used to build happens-before semantics. That is to say, the C/C++ Volatile mentioned earlier is moreThe wrong usage scenario under the thread is right in the Java language. As shown in the following figure:
VolatileThe origin of
C/C++There are three characteristics of Volatile Keywords: variability, non optimization, and sequentially. So, why is Volatile designed to be like this? To answer this question, we need to start with the generation of Volatile keywords. (Note: the content of this section, reference from C++ and the Perils of Double-Checked Locking the tenth chapter of the paper: volatile:A Brief History. This is a good paper on the top. It is worth reading many times and highly recommended. )
VolatileKeywords, first appeared in 1870s, was used to deal with problems caused by memory-mapeed I/O (MMIO). After MMIO is introduced, a memory address may be real memory or may be mapped to a I/O port. RelativeTo read and write a memory address, it is possible to manipulate memory, or to read and write a I/O device. Why does MMIO need to introduce Volatile keywords? Consider a code fragment as follows:
In this code fragment, pointer P may either point to a memory address or point to a I/O device. If the pointer P points to the I/O device, (1), the A and B in (2) will receive two consecutive bytes of the I/O device. However, P can also point to memory.At the same time, the compiler’s optimization strategy may determine that a and b read data from the same memory address at the same time. After finishing (1), assign a to B directly. For I/O devices, it is necessary to prevent the compiler from doing this optimization. It is not possible to assume that pointer B points to the same content – changeability.
Similarly, the code (3), (4) has a similar problem, and the compiler finds that it is meaningless to assign a, B to the pointer P at the same time, so it may optimize the assignment operation in code (3) and only retain the code (4). For I/O devices, it is necessary to prevent the compiler from completely optimizing the write operation.- “no optimization”.
For I/O devices, the compiler can’t arbitrarily interact with the order of instructions, because the contents of the I/O device change – “sequentially”, as the order changes.
Based on the three requirements of MMIO, the designed C/C++ Volatile keyword, which contains the features, is the three characteristics of the previous analysis in this article: variability; non optimability; sequence.