Deep multithreading: Analysis on the use of memory barrier and volatile keywords

Source: Internet
Author: User

In some simple examples, for example, to assign a value to a field or increment the field, we need to synchronize the thread,
Although the lock can meet our needs, a competitive lock will certainly lead to blocking, and then endure the thread context switching and scheduling overhead. In some highly concurrent and performance-critical aspects, these are intolerable.
. Net framework provides a non-blocking synchronization structure, which improves performance for some simple operations. It does not even block, pause, or wait for the thread.

Memory Barriers and Volatility (Memory fence and loss-prone fields)
Consider the following code:

Copy codeThe Code is as follows: int _ answer;
Bool _ complete;
Void ()
{
_ Answer = 123;
_ Complete = true;
}
Void B ()
{
If (_ complete)
Console. WriteLine (_ answer );
}

If Methods A and B are concurrently executed in different threads, May Method B output "0?

The answer is "yes" for the following reasons:
The compiler, clr, or cpu may re-sort program instructions for performance. For example, the order of the two statements in method A may be adjusted.
The compiler, clr, or cpu may use A cache policy to assign values to variables, so that these variables are not immediately visible to other variables. For example, assign values to variables in method, does not immediately refresh to the memory. The variable B sees is not the latest value.

C # Be careful when running the program to ensure that these optimization policies do not affect normal single-threaded code or code that is locked in a multi-threaded environment.
In addition, you must create Memory fences to limit the impact of Command Re-sorting and read/write cache on the program.

Full fences:

The simplest way to complete the fence is to use the Thread. MemoryBarrier method.

The following is an explanation of msdn:
Thread. memoryBarrier: Synchronize memory access as follows: when the processor executing the current thread sorts the commands again, the memory access after the MemoryBarrier call is not allowed, then execute MemoryBarrier to call the previous memory access method.
According to my personal understanding: After writing the data, call MemoryBarrier to refresh the data immediately. In addition, call MemoryBarrier before reading the data to ensure that the read data is up-to-date, and the processor must be careful with MemoryBarrier optimization.

Copy codeThe Code is as follows: int _ answer;
Bool _ complete;
Void ()
{
_ Answer = 123;
Thread. MemoryBarrier (); // After writing, create a memory fence.
_ Complete = true;
Thread. MemoryBarrier (); // After writing, create a memory fence.
}
Void B ()
{
Thread. MemoryBarrier (); // create a memory fence before reading
If (_ complete)
{
Thread. MemoryBarrier (); // create a memory fence before reading
Console. WriteLine (_ answer );
}
}

A Full fence in modern desktop applications takes more than 10 nanoseconds.
Some of the following structures implicitly generate a Full fence.

C # Lock Statement (Monitor. Enter/Monitor. Exit)
All methods in the Interlocked class.
Use asynchronous callback of the thread pool, including asynchronous delegation, APM callback, and Task continuations.
Settings and waiting in a Signal Construction)

You do not need to use full barrier for reading and writing every variable. If you have three answer fields, we can still use four barrier fields. For example:

Copy codeThe Code is as follows: int _ answer1, _ answer2, _ answer3;
Bool _ complete;
Void ()
{
_ Answer1 = 1; _ answer2 = 2; _ answer3 = 3;
Thread. MemoryBarrier (); // After writing, create a memory fence.
_ Complete = true;
Thread. MemoryBarrier (); // After writing, create a memory fence.
}
Void B ()
{
Thread. MemoryBarrier (); // create a memory fence before reading
If (_ complete)
{
Thread. MemoryBarrier (); // create a memory fence before reading
Console. WriteLine (_ answer1 + _ answer2 + _ answer3 );
}
}

Do we really need lock and memory barrier?
If you do not use lock or barrier on a shared writable field, you may find yourself in trouble. On msdn, there are many things about this.
Consider the following code:Copy codeThe Code is as follows: public static void Main ()
{
Bool complete = false;
Var t = new Thread () =>
{
Bool toggle = false;
While (! Complete) toggle =! Toggle;
});
T. Start ();
Thread. Sleep (1000 );
Complete = true;
T. Join ();
}

If you select release mode in Visual Studio to generate the application, the application will not be aborted if you run the application directly.
Because the CPU register caches the value of the complete variable. In registers, the complete is always false.
This problem can be solved by inserting Thread. MemoryBarrier in the while loop or locking when reading complete.

Volatile keywords
Adding the volatile keyword to the _ complete field can also solve this problem.
Volatile bool _ complete.

The Volatile keyword will guide the compiler to automatically add barriers to read/write fields. The following is an explanation of msdn:
The volatile keyword indicates that a field can be modified by multiple concurrent threads. Fields declared as volatile are not restricted by Compiler Optimization (assuming that they are accessed by a single thread. This ensures that the field displays the latest value at any time.

The volatile field can be summarized into the following table:

Command 1

Second instruction

Can it be exchanged?

Read

Read

No

Read

Write

No

Write

Write

No (CLR ensures that write and write operations are not exchanged, or even the volatile keyword is not used)

Write

Read

Yes!

Note that the volatile keyword is applied, and the subsequent write and read operations cannot be exchanged, which may cause inexplicable problems. For example:Copy codeThe Code is as follows: volatile int x, y;
Void Test1 ()
{
X = 1; // Volatile write
Int a = y; // Volatile Read
}

Void Test2 ()
{
Y = 1; // Volatile write
Int B = x; // Volatile Read
}

If Test1 and Test2 are concurrently executed in different threads, it is possible that the values of field a and field B are both 0 (although the volatile keyword is applied on x and y)

This is a good example to avoid using the volatile keyword. Even if you fully understand this code, do other people who work on your code understand it ?.

Using Full fences or locks in the Test1 and Test2 methods can solve this problem,

Another reason for not using the volatile keyword is the performance problem, because each read/write creates a memory fence, for example

Copy codeThe Code is as follows: volatile m_amount
M_amount = m_amount + m_amount.

The Volatile keyword does not support referencing passed parameters and local variables. In this scenario, you must use

VolatileRead and VolatileWrite methods. For example

Copy codeThe Code is as follows:

Volatile int m_amount;
Boolean success = int32.TryParse ("123", out m_amount );
// Generate the following warning information:
// Cs0420: Reference to the volatile field is not considered volatile.

VolatileRead and VolatileWrite

Technically speaking, the static methods of the Thread class VolatileRead and VolatileWrite play the same role in reading a variable and the volatile keyword.

Their implementation is equally inefficient, even though they have actually created a memory fence. The following is their implementation on the integer type.

Copy codeThe Code is as follows: public static void VolatileWrite (ref int address, int value)
{
Thread. MemoryBarrier (); address = value;
}

Public static int VolatileRead (ref int address)
{
Int num = address; Thread. MemoryBarrier (); return num;
}

As you can see, if you call VolatileRead after calling VolatileWrite, there is no fence in the middle and it will be created, which will also lead to the possible change of reading order after writing.

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.