. Net (C #): rediscuss value type-atomicity

Source: Internet
Author: User

I wrote an article about understanding the value type and reference type two years ago, mainly about the differences between common value types and reference types. However, these two types have more origins than that. Today, let's talk about the atomicity of the value type's online path.

Directory

  • 0. Concepts
  • 1. Interlocked type
  • 2. Use the reference type to declare
  • 3. binning and unboxing
  • 4. Use lock

 

Returned directory
0. Concepts

First of all, I would like to emphasize that "atomicity" and "thread security" are obfuscation-prone concepts and solve the problem that "atomicity" is not equal to solving "thread security ", however, if "atomicity" is not guaranteed, it is certainly not "thread security. "Atomicity" is considered a category of "thread security". This article only discusses "atomicity.

 

According to the C # language standard, only the following types of read/write are atomic:

Bool, Char, byte, sbyte, short, ushort, uint, Int, float, reference type, and the above value types are used as underlying enumeration types.

However, note that these types of operations (including increment ++ and decrease --) are not atomic.

 

But according to the CLI standard.

I .12.6.6 (atomic reads and writes): CLI should ensure that the following types of read/write are atomic:

The correct location in the memory, and the size is not greater than the word size that the CPU processes at a time (that is, the so-called word size, equal to the local int size ).

 

It can be inferred that in a 64-bit CPU environment, 64-bit built-in types (such as long, ulong, and double) read and write are also atomic. This is described in Eric lippert's blog. However, Eric lippert also mentioned in his article that some non-standard CLI execution environments may break this inference because this is a CLI rule, not a C # language rule. Therefore, for these special types, I personally think it is best to use the interlocked operation or lock. The content is described in detail below.

 

The following describes how to try to solve the value type atomicity. The more the above method is, the more recommended it is.

 

Returned directory
1. Interlocked type

To solve the atomic problem of value types, the first type is system. Threading. interlocked. For example, a method Member of the interlocked type:

As you can see, the interlocked type provides various forms of atomic operations: read, write, increment, and decrease. Multiple types are also supported.

 

The interlocked type provides a way to reduce the use of lock to increase performance. For example, to add atomic operations to a counter, there is no need to use lock like this:

Readonly object locker = new object ();

 

Int myvar;

// Method for performing atomic value-added operations

Public void atomicincrement (INT increment)

{

Lock (locker)

{

Myvar + = increment;

}

}

 

You can use the interlocked type to perform this atomic operation. The following code is recommended:

Int myvar;

// Method for performing atomic value-added operations

Public void atomicincrement (INT increment)

{

Interlocked. Add (ref myvar, increment );

}

This not only increases performance, but also reduces code and does not need to declare the object used for lock.

 

Therefore, if interlocked can be used, try to use the interlocked type to solve the problem. Of course, there will certainly be methods not supported by the interlocked type. For example, the read method only supports the long parameter and does not have double. (This problem will be resolved later)

 

Returned directory
2. Use the reference type to declare

For example, a value type:

Struct mystruct

{

Public long data1;

Public double data2;

}

 

Obviously, the occupied space of this type of object must be greater than or equal to 128 bits. In both 32-bit and 64-bit CPU environments, this type of read/write operations are not atomic. Moreover, the interlocked type does not support user-defined struct. How can we make it atomic?

In fact, if you can, the best way here is to change it to class. Yes, reading and writing of the reference type is always atomic. In this case, we still do not need to use lock.

// Change mystruct to myclass. This type of read/write is atomic.

Class myclass

{

Public long data1;

Public double data2;

}

 

Returned directory
3. binning and unboxing

Suppose we cannot define the above mystruct struct as a reference type. How can we solve the problem of atomic read/write? Another trick is to describe the problem first. Let's first declare the attributes of a mystruct struct:

Public mystruct mystructobject {Get; set ;}

 

Obviously, the read and write operations of the mystruct struct are not Atomic. If one thread is writing the mystructobject attribute while the other thread is reading the mystructobject variable, unexpected values may be output, it is possible that when one thread writes half of the data, the other thread starts the read operation. Therefore, the value read by another thread through the mystructobject attribute may not be the value before or after the write, but partially write the original incomplete state of some data.

// A thread is writing

Mystructobject = new mystruct () {data1 = 2, data2 = 3 };

 

// Another thread is reading

// The output is not the value before or after writing, but the incomplete status of some original data!

Console. writeline ("{0} {1}", mystructobject. data1, mystructobject. data2 );

 

The trick is to use packing and unpacking to declare attributes as objects, as follows:

// Object declaration type

// Read and write mystruct objects need to be packed and unpacked

Public static object mystructobject {Get; set ;}

 

In this way, if the preceding multi-threaded read/write code is encountered again, there will be no problem (note that a forced conversion is required when reading the mystructobject attribute, that is, the packing operation ), at this time, the read and write operations will be packed and unboxed. The final operation is the object reference object, and its read and write operations are atomic.

 

Returned directory
4. Use lock

All the methods mentioned above have their own advantages, but they are not omnipotent. If you use lock, although there is extra performance loss, it is omnipotent. So if there are any non-atomic operations, you can nest them into a lock, so that other threads will not interrupt these operations, the whole process is atomic!

For example, in a 32-bit CPU environment, the double addition operation of the atom can be performed as follows:

Readonly object locker = new object ();

 

Double mydouble;

// Method for performing atomic value-added operations

Public void atomicincrement (double increment)

{

// The interlocked. Add method does not support the double type !!!

Lock (locker)

{

Mydouble + = increment;

}

}

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.