malaikuangren

What is the purpose or drive to build thing like (xxx),How can it achieve the original goal of design?
Thread-Safety with the Interlocked Class(Synchronization of .net)

Thread-Safety with the Interlocked Class

When developing multi-threaded software or using parallel programming techniques, it is essential that classes remain thread-safe. The Interlocked class provides methods that assist with thread safety by performing atomic operations. 

Thread-Safety

When you are using the task parallel library to parallelise your code, or when writing multi-threaded applications, you must ensure that your code is thread-safe. This means that any data that is shared by multiple threads must be accessed in a manner that ensures that threads cannot interfere with each other's results.

Some standard operations provided by C# and the .NET framework are not thread-safe, even though they may appear to be. For example, you might assume that the increment operator(++) performs an atomic operation. However, in reality incrementing a variable's value involves reading the value, updating the read value and writing the updated value back into the variable. If two threads attempt to increment a value simultaneously a race condition may occur and it is possible that the value be increased only by one, rather than two.

To demonstrate this problem, and for the other examples in this article, we will use methods from the Parallel class. To begin, create a console application and add the following using directives to the code. NB: If you are using an earlier version of the .NET framework than 4.0, you can create similar examples using standard multi-threading code. (4.0之前的版本没有并行计算功能)

using System.Threading;
using System.Threading.Tasks;

The following code executes a parallel for loop with one million increment operations, each changing the value of a shared variable. The final value of the variable should be one million. However, on a computer with multiple cores or processors, you will likely see a much lower result, caused by the increment operator not being thread-safe.

int total = 0;
Parallel.For(0, 1000000, i =>
{
    total++;
});
Console.WriteLine(total);   // Unlikely to be 1,000,000(结果不会是1,000,000)

 

Interlocked Class

The Interlocked class provides a number of static methods that perform atomic operations. These can generally be regarded as thread-safe. The class is found in the System.Threading namespace.

Increment and Decrement

Two commonly used Interlocked methods are Increment and Decrement. As their names suggest, these methods increase or decrease a variable's value by one, in a similar manner to the increment (++) and decrement (--) operators. Both methods work with either a 32-bit or 64-bit integer, which is passed to the method using a reference parameter.

The code below shows a thread-safe version of the earlier example. In this parallel loop the million operations produce the correct result.

int total = 0;
Parallel.For(0, 1000000, i =>
{
    Interlocked.Increment(ref total);
});
Console.WriteLine(total);   // 1,000,000

Add

The Add method was introduced with the .NET framework version 2.0. It performs an atomic, thread-safe addition of two values. The first parameter accepts a 32-bit or 64-bit integer variable, passed by reference. The second parameter accepts a second integer, which will be added to the first.

The sample below shows the Add method being used successfully within a parallel loop.

int total = 0;
Parallel.For(0, 1000000, i =>
{
    Interlocked.Add(ref total, 5);
});
Console.WriteLine(total);   // 5,000,000

Exchange

The Exchange method originally worked with 32-bit integers, single-precision floating-point numbers or objects. This was extended to a greater choice of data types, including generic types, with .NET 2.0. The method accepts two arguments of the same type. The first, passed by reference, is changed to match that of the second and the original value is returned.

The code below shows the syntax of the method and some sample results.

int value = 1;
int newValue = 2;
 
int oldValue = Interlocked.Exchange(ref value, newValue);
 
Console.WriteLine(value);       // 2
Console.WriteLine(oldValue);    // 1

CompareExchange

CompareExchange provides a similar operation to Exchange, in that it can replace one variable's value with an alternative value as an atomic operation. The difference is that the exchange is only made if a comparison with a third value determines equality.

The first parameter is the value that may be replaced. It is passed by reference to allow the original variable to be updated. The second parameter holds the value that the first may be replaced with. The third argument specifies a value to be compared with that of the first. If the two values match, the exchange is made. If not, the original value remains. In either case, the return value of the method is the original value of the first parameter.

The sample code below shows the CompareExchange method being called twice. In the first instance the comparison yields a match so the original value is replaced. In the second call the values do not match so the primary value remains the same.

int value = 1;
int compare = 1;
int compare2 = 5;
int newValue = 99;
 
int oldValue = Interlocked.CompareExchange(ref value, newValue, compare);
Console.WriteLine(value);       // 99 - changed
Console.WriteLine(oldValue);    // 1
 
value = 1;
oldValue = Interlocked.CompareExchange(ref value, newValue, compare2);
Console.WriteLine(value);       // 1 - unchanged
Console.WriteLine(oldValue);    // 1

Read

Surprisingly, reading the value of a 64-bit integer is not an atomic operation and, therefore, is not thread-safe. When executing code on a 64-bit processor such reads are atomic and thread-safe. However, when the computer has a 32-bit processor such a read requires separate 32-bit reads from two locations. This means that it is possible, if one thread changes the 64-bit value whilst another is reading it, that simply reading a 64-bit value will yield an incorrect result.

The Read method is available in the .NET framework version 2.0 and later. It ensures that when you read a 64-bit value that you do so in a thread-safe manner. The syntax of the method is as follows:

long value = Interlocked.Read(ref valueToRead);

Interlocked Considerations

All of the Interlocked methods are thread-safe with respect to each other. However, they do not guarantee thread safety when used with other operations. For example, if one thread uses the Increment method whilst another uses the increment operator, it is still possible to get incorrect results. Therefore, when you are using Interlocked methods against shared data you should ensure that all access to that data uses Interlocked methods. If this is not viable, you should use means to synchronise your threads, such as locking critical sections with the lock statement. 

看了上面的这些知识给我感觉,其实.net编程中,存在很多多线程的陷井,只是平时没有意识到罢了。 

posted on 2012-06-02 14:45  malaikuangren  阅读(406)  评论(0编辑  收藏  举报