Several methods of C # thread synchronization

Source: Internet
Author: User
Tags exit in mutex readline volatile

On the internet also read some about thread synchronization of the article, in fact, there are several methods of thread synchronization, the following I will simply do a summary.

  First, the volatile keyword

Volatile is the simplest method of synchronization, and of course, it's a price to pay. It can only be synchronized at the variable level, the meaning of volatile is to tell the processor, do not put me into the working memory, please directly in main memory operation me. ("www.bitsCN.com") therefore, when multithreading accesses the variable at the same time, it will manipulate the main memory directly and do the variable sharing in essence.

The number of types that can be identified as volatile must be the following: (excerpt from MSDN)

    • Any reference type.
    • Any pointer type (under an unsafe context).
    • The types sbyte, Byte, short, ushort, int, uint, char, float, bool.
    • An enum type with a enum base type of Byte, SByte, short, ushort, int, or UINT.

Such as:

public class a{    private volatile int _i;    public int I    {        get {return _i;}        set {_i = value;}}    }

  

However, volatile does not achieve true synchronization because its operating level is only at the variable level, not at the atomic level. If it is in a single processor system, there is no problem, the variable in main memory has no chance to be modified by others, because there is only one processor, which is called Processor self-consistency. However, in a multiprocessor system, there may be a problem. Each processor has its own data cach, and the updated data is not necessarily immediately written back to main memory. So it can be a different step, but it's very difficult to happen because the Cach read and write very fast, flush frequency is also very high, only when the stress test can occur, and the probability is very very small.

  Second, lock keyword

Lock is a simple and easy way to synchronize threads by acquiring mutexes for a given object. It ensures that when a thread is in a critical section of code, another thread does not come in, it waits until the thread object is freed, that is, the thread is out of the critical section. Usage:

public void Function () {    Object lockThis = new Object ();    Lock (LockThis)    {        //Access thread-sensitive resources.    }}

  

The parameter of lock must be based on the reference type of the object, not the basic type like bool,int, so it is not synchronous at all, because the parameters of lock is the object, if the incoming int, it is bound to take place boxing operation, so each lock will be a new different object. It is best to avoid using the public type or an object instance that is not controlled by the program because it is likely to cause deadlocks. In particular, do not use a string as a parameter to lock because the string is "persisted" by the CLR, meaning that there is only one instance of the string given in the entire application, so it is more likely to cause deadlocks. It is recommended that you use a private or protected member that is not "persisted" as a parameter. In fact, some classes have already provided dedicated members to be locked, such as the array type provides SyncRoot, and many other collection types also provide syncroot.

Therefore, use lock should pay attention to the following points:

1, if the instance of a class is public, it is best not to lock (this). Because the person who uses your class may not know that you are using lock, and if he has a new instance, and locks the instance, it can easily cause a deadlock.

2, if the MyType is public, do not lock (typeof (MyType))

3. Never lock a string

Third, System.Threading.Interlocked

For simple operations of integer data types, you can use the members of the Interlocked class to implement thread synchronization, which exists in the System.Threading namespace. The Interlocked class has the following methods: Increment, Decrement, Exchange, and CompareExchange. Using increment and decrement guarantees that an integer can be added and reduced to an atomic operation. The Exchange method automatically swaps the value of the specified variable. The CompareExchange method combines two actions: comparing two values and storing the third value in one of the variables based on the result of the comparison. The comparison and interchange operations are also performed by atomic operations. Such as:

int i = 0; System.Threading.Interlocked.Increment (ref i); Console.WriteLine (i); System.Threading.Interlocked.Decrement (ref i); Console.WriteLine (i); System.Threading.Interlocked.Exchange (ref i, 100); Console.WriteLine (i); System.Threading.Interlocked.CompareExchange (ref I, 10, 100);

  

Output:

  Iv. Monitor

The Monitor class provides functionality similar to lock, but unlike lock, it has better control over the synchronization block, and when the Enter (object o) method of Monitor is called, the exclusive right of O is obtained until the exit (object O) method is called. To release exclusive rights to O, you can call the Enter (object o) method multiple times, just call the same number of exit (object O) methods, and the Monitor class provides an overloaded method of TryEnter (object O,[int]). This method attempts to acquire the exclusive rights of an O object, and returns false when the acquisition of exclusive rights fails.

However, using lock is generally preferable to using Monitor directly, on the one hand because lock is more concise and, on the other hand, because lock ensures that even protected code throws an exception, You can also release the base monitor. This is done by calling exit in finally . In fact,lock is implemented using the Monitor class. The following two sections of code are equivalent:

Object x = new Object (); Lock (x) {    //dosomething ();} Equivalent to Object obj = (object) x; System.Threading.Monitor.Enter (obj); try{    //dosomething ();} finally{    System.Threading.Monitor.Exit (obj);}

  

For usage, refer to the following code:

private static Object M_monitorobject = new Object (); [stathread]static void Main (string[] args) {    thread thread = new Thread (new ThreadStart (DO));    Thread. Name = "Thread1";    Thread thread2 = new Thread (new ThreadStart (DO));    Thread2. Name = "Thread2";    Thread. Start ();    Thread2. Start ();    Thread. Join ();    Thread2. Join ();    Console.read ();} static void Do () {    if (! Monitor.TryEnter (m_monitorobject))    {        Console.WriteLine ("Can ' t visit Object" + Thread.CurrentThread.Name) ;        return;    }    Try    {        monitor.enter (m_monitorobject);        Console.WriteLine ("Enter Monitor" + Thread.CurrentThread.Name);        Thread.Sleep ();    }    Finally    {        monitor.exit (m_monitorobject);    }}

  

When thread 1 acquires the M_monitorobject object exclusive, thread 2 attempts to call TryEnter (M_monitorobject), which returns false due to the inability to acquire exclusive rights, with the following output information:

In addition, Monitor provides three static methods Monitor.pulse (Object o), Monitor.pulseall (object o) and Monitor.Wait (Object o), which are used to implement a wake-up mechanism for synchronization. For the use of these three methods, you can refer to MSDN, which is not detailed here.

  V. mutexes

In use, the mutex is closer to the above monitor, but the mutex does not have the Wait,pulse,pulseall function, so we cannot use the mutex to implement a similar wake-up function. However, mutexes have a relatively large feature, and mutexes are cross-process, so we can use the same mutex on multiple processes on the same machine or even a remote machine. Although the mutex can also implement in-process thread synchronization and is more powerful, it is recommended to use monitor in this case, because the mutex class is Win32 encapsulated, so it requires an interoperability transformation that consumes more resources.

  Liu, ReaderWriterLock

In the case of resource access, we implement the lock mechanism on the resource, but in some cases we just need to read the data of the resource, rather than modify the data of the resource, and in such cases the exclusive right to get the resource will undoubtedly affect the operational efficiency, therefore. NET provides a mechanism for resource access using ReaderWriterLock, if a resource does not acquire write exclusive rights at a time, then multiple read access rights can be obtained, exclusive rights for a single write, and if the exclusive right to write has been acquired at a moment, Then the other read access must wait, referring to the following code:

private static ReaderWriterLock M_readerwriterlock = new ReaderWriterLock ();p rivate static int m_int = 0;    [stathread]static void Main (string[] args) {Thread readthread = new Thread (new ThreadStart (Read));    Readthread.name = "ReadThread1";    Thread readThread2 = new Thread (new ThreadStart (Read));    Readthread2.name = "ReadThread2";    Thread writethread = new Thread (new ThreadStart (Writer));    Writethread.name = "Writerthread";    Readthread.start ();    Readthread2.start ();    Writethread.start ();    Readthread.join ();    Readthread2.join ();    Writethread.join (); Console.ReadLine ();} private static void Read () {while (true) {Console.WriteLine ("ThreadName" + Thread.CurrentThread.Name + "Ac        Quirereaderlock ");        M_readerwriterlock.acquirereaderlock (10000);        Console.WriteLine (String.Format ("ThreadName: {0} m_int: {1}", Thread.CurrentThread.Name, M_int));    M_readerwriterlock.releasereaderlock ();   }}private static void Writer () {while (true) {Console.WriteLine ("ThreadName" + Thread.CurrentThread.Name + "AcquireWriterLock");        M_readerwriterlock.acquirewriterlock (1000);        Interlocked.Increment (ref m_int);        Thread.Sleep (5000);        M_readerwriterlock.releasewriterlock ();    Console.WriteLine ("ThreadName" + Thread.CurrentThread.Name + "Releasewriterlock"); }}

  

In the program, we start two threads to get read access to the M_int, use a thread to get the write exclusive rights of the M_int, and after executing the code, the output is as follows:

As you can see, any other read thread must wait until Writerthread acquires the write exclusive rights, until Writerthread releases the write exclusive to gain access to the data, it should be noted that the above printing information clearly shows that It is possible for multiple threads to fetch the data at the same time, as can be seen from the output of the ReadThread1 and ReadThread2 information interactions.

  Seven, SynchronizationAttribute

When we determine that an instance of a class can only be accessed by one thread at a time, we can directly identify the class as synchronization, so that the CLR automatically implements the synchronization mechanism for this class, which in fact involves the concept of a synchronous domain, when the class is designed as follows We can ensure that an instance of a class cannot be accessed concurrently by multiple threads
1). In the declaration of the class, add the System.Runtime.Remoting.Contexts.SynchronizationAttribute property.
2). Inherit to System.ContextBoundObject
It should be noted that to implement the above mechanism, the class must inherit to System.ContextBoundObject, in other words, the class must be context bound.
A demonstration class code is as follows:

[System.Runtime.Remoting.Contexts.Synchronization]
public class SynchronizedClass:System.ContextBoundObject
{

}

Eight,MethodImplAttribute

If the critical section spans the entire method, that is, the code inside the entire method needs to be locked, it is easier to use the MethodImplAttribute property. So you don't have to lock inside the method, just add [MethodImpl (methodimploptions.synchronized)] on the method, Both Mehthodimpl and methodimploptions are inside the namespace System.Runtime.CompilerServices. Note, however, that this property locks the entire method until the method returns, releasing the lock. Therefore, the use is not very flexible. If you want to release the lock early, you should use monitor or lock. Let's look at an example:

[MethodImpl (methodimploptions.synchronized)]public void Dosomeworksync () {Console.WriteLine ("DoSomeWorkSync ()--    Lock held by Thread "+ Thread.CurrentThread.GetHashCode ());    Thread.Sleep (1000); Console.WriteLine ("Dosomeworksync ()--lock released by Thread" + Thread.CurrentThread.GetHashCode ());} public void Dosomeworknosync () {Console.WriteLine ("Dosomeworknosync ()--entered Thread is" + thread.currentthread .    GetHashCode ());    Thread.Sleep (1000); Console.WriteLine ("Dosomeworknosync ()--leaving Thread is" + Thread.CurrentThread.GetHashCode ());}    [stathread]static void Main (string[] args) {methodimplattr testobj = new Methodimplattr ();    thread T1 = new Thread (new ThreadStart (Testobj.dosomeworknosync));    Thread t2 = new Thread (new ThreadStart (Testobj.dosomeworknosync)); T1.    Start (); T2.    Start ();    thread t3 = new Thread (new ThreadStart (Testobj.dosomeworksync));    thread T4 = new Thread (new ThreadStart (Testobj.dosomeworksync)); T3.  Start ();  T4.    Start (); Console.ReadLine ();}

  

Here, we have two methods, we can compare, one is to add the attribute MethodImpl Dosomeworksync (), one is not added Dosomeworknosync (). Sleep (1000) in the method is intended to allow enough time for the second thread to come in when the first thread is still in the method. Starting with two threads for each method, let's take a look at the results:

As you can see, for threads 1 and 2, that is, the thread that calls the method without attributes, when thread 2 enters the method, has not left, thread 1 has come in, that is, the method is not synchronized. Let's take a look at threads 3 and 4, when thread 3 comes in, the method is locked until thread 3 releases the lock, and thread 4 comes in.

  Ix. synchronization events and wait handles

Lock and monitor are good for thread synchronization, but they cannot deliver events between threads. Synchronization events are used when thread synchronization is to be implemented and there is an interaction between the threads. A synchronization event is an object that has two states (both terminating and non-terminating) that can be used to activate and suspend threads.

There are two types of synchronization events: AutoResetEvent and ManualResetEvent. The only difference between them is whether the state automatically changes from terminating to non-terminating after activating the thread. AutoResetEvent automatically becomes non-terminating, meaning that a autoresetevent can only activate one thread. The ManualResetEvent waits until its reset method is called, and the state becomes non-terminating, before which the ManualResetEvent can activate any number of threads.

You can call WaitOne, WaitAny, or WaitAll to make the thread wait for the event. The difference between them can be seen on MSDN. When the set method of the event is called, the event becomes signaled and the waiting thread is awakened.

Look at an example, this example is on MSDN. Because the event is only used for the activation of one thread, it is possible to use the AutoResetEvent or manualresetevent classes.

static AutoResetEvent autoevent;static void DoWork () {    Console.WriteLine ("Worker thread started, now waiting on event ");    Autoevent.waitone ();    Console.WriteLine ("Worker thread reactivated, now exiting");} [stathread]static void Main (string[] args) {    autoevent = new AutoResetEvent (false);    Console.WriteLine ("Main thread starting worker thread");    Thread t = new Thread (new ThreadStart (DoWork));    T.start ();    Console.WriteLine ("Main Thrad sleeping for 1 second");    Thread.Sleep (+);    Console.WriteLine ("Main thread signaling worker thread");    Autoevent.set ();    Console.ReadLine ();}

  

Let's look at the output first:

In the main function, first create an instance of AutoResetEvent, the parameter false indicates that the initial state is non-terminating, and if true, the initial state is terminated. It then creates and starts a child thread that, in a child thread, waits for the specified event to occur by calling the WaitOne method of AutoResetEvent. After the main thread waits one second, the AutoResetEvent's set method is called, which changes the state from non-terminating to terminating and reactivating the child thread.

Several methods of C # thread synchronization

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.