Multi-thread synchronization

Source: Internet
Author: User

One advantage of using multiple threads in an application is that each thread can be executed asynchronously. For Windows applications, time-consuming tasks can be executed in the background, so that the application Windows and controls remain responsive. For server applications, multithreading allows different threads to process each incoming request. Otherwise, each new request cannot be processed until the previous request is fully satisfied.

However, the asynchronous nature of threads means that access to resources (such as file handles, network connections, and memory) must be coordinated. Otherwise, two or more threads may access the same resource at the same time, and each thread does not know the operations of other threads. Unexpected data corruption occurs.

For simple operations on integer data types, you can use Interlocked class members to implement thread synchronization. For all other data types and non-thread-Safe Resources, only the structures in this topic can be used to safely perform multi-threaded processing (from msdn ).

1: lock keyword

The lock keyword can be used to ensure that the code block is executed without being interrupted by other threads. This is achieved by obtaining mutex locks for a given object during the code block operation.

The lock statement starts with the keyword lock. It has an object as a parameter, and there is a code block that can only be executed by one thread at a time after this parameter. For example:

C #
Public void Function ()
{
System. Object lockThis = new System. Object ();
Lock (lockThis)
{
// Access thread-sensitive resources.
}
} The parameter provided to the lock keyword must be an object of the reference type, which is used to define the lock range. In the above example, the lock range is limited to this function because no reference to this object exists outside the function. Strictly speaking, the object provided to lock is only used to uniquely identify resources shared by multiple threads, so it can be any class instance. However, in fact, this object usually indicates the resources that require thread synchronization. For example, if a container object is used by multiple threads, the container can be passed to lock, and the synchronization code block after lock will access the container. As long as other threads lock the container before accessing the container, access to the object will be synchronized securely.

Generally, it is better to avoid locking the public type or locking an object instance that is not controlled by the application. For example, if the instance can be publicly accessed, lock (this) may be faulty because uncontrolled Code may also lock the object. This may cause a deadlock, that is, two or more threads are waiting to release the same object. For the same reason, locking public data types (compared to objects) may also cause problems. Locking a string is especially dangerous because the string is "Temporarily" by the Common Language Runtime Library (CLR ". This means that there is only one instance for any given string in the entire program, and the same object represents the text in all threads in all running application domains. Therefore, as long as a lock is placed on a string with the same content anywhere in the application process, all instances of the string in the application will be locked. Therefore, it is best to lock private or protected members that are not temporarily retained. Some classes provide members dedicated for locking. For example, the Array type provides SyncRoot. Many Collection types also provide SyncRoot.

2: Monitor

Similar to the lock keyword, the monitor prevents multiple threads from executing code blocks simultaneously. The Enter method allows only one thread to continue executing the subsequent statements. All other threads will be blocked until the thread that executes the statement calls Exit. This is the same as using the lock keyword. In fact, the lock keyword is implemented using the Monitor class. For example:

C #
Copy code
Lock (x)
{
DoSomething ();
} This is equivalent:

C #
Copy code
System. Object obj = (System. Object) x;
System. Threading. Monitor. Enter (obj );
Try
{
DoSomething ();
}
Finally
{
System. Threading. Monitor. Exit (obj );
} Using the lock keyword is generally more desirable than directly using the Monitor class. On the one hand, it is because the lock is more concise, and on the other hand, it ensures that the basic Monitor can be released even if the protected code causes exceptions. This is achieved through the finally keyword. It executes the associated code block regardless of whether an exception is thrown.

For more information about the monitor, see the monitor synchronization technical example.

Synchronization event and wait handle
The use of locks or monitors is useful to prevent simultaneous execution of code blocks that distinguish threads, but these constructs do not allow one thread to send events to another thread. This requires a "synchronization event", which is an object in two States (termination and non-termination) and can be used to activate and suspend threads. Enables a thread to wait for non-terminated synchronization events to suspend the thread, and changes the event status to terminate to activate the thread. If the thread tries to wait for the terminated event, the thread continues to execute without delay.

There are two types of synchronization events: AutoResetEvent and ManualResetEvent. The only difference between them is that, at any time, as long as the AutoResetEvent activates the thread, its status will automatically change from terminating to non-terminating. On the contrary, ManualResetEvent allows its termination state to activate any number of threads. It is restored to a non-terminating State only when its Reset method is called.

A thread can wait for an event by calling a wait method, such as WaitOne, WaitAny, or WaitAll. System. threading. waitHandle. waitOne enables the thread to wait until a single event changes to the terminated state. System. threading. waitHandle. waitAny stops the thread until one or more indicated events change to the terminated state. System. threading. waitHandle. waitAll stops the thread until all indicated events change to the terminated state. When the Set Method of the event is called, the event changes to the terminated state.

In the following example, a thread is created and started by the Main function. The new thread uses the WaitOne method to wait for an event. Before the event is terminated by the Main thread executing the Main function, the thread remains suspended. Once the event is terminated, the secondary thread returns. In this example, because the event is only used for activation by one thread, you can use the AutoResetEvent or ManualResetEvent class.

C #
Copy code
Using System;
Using System. Threading;

Class ThreadingExample
{
Static AutoResetEvent autoEvent;

Static void DoWork ()
{
Console. WriteLine ("worker thread started, now waiting on event ...");
AutoEvent. WaitOne ();
Console. WriteLine ("worker thread reactivated, now exiting ...");
}

Static void Main ()
{
AutoEvent = new AutoResetEvent (false );

Console. WriteLine ("main thread starting worker thread ...");
Thread t = new Thread (DoWork );
T. Start ();

Console. WriteLine ("main thrad sleeping for 1 second ...");
Thread. Sleep (1000 );

Console. WriteLine ("main thread signaling worker thread ...");
AutoEvent. Set ();
}
} 3: Mutex object

Mutex is similar to monitor; it prevents multiple threads from executing a code block at a time. In fact, the name "mutex" is short for the term "mutually exclusive. However, unlike the monitor, mutex can be used to synchronize threads across processes. Mutex is represented by the Mutex class.

When used for inter-process synchronization, mutex is called "named mutex" because it will be used for another application, so it cannot be shared through global variables or static variables. You must specify a name for the object to allow the two applications to access the same mutex object.

Although mutex can be used for intra-process thread synchronization, it is generally more desirable to use Monitor because the Monitor is designed specifically for. NET Framework, so it can make better use of resources. In contrast, the Mutex class is a Win32 constructed package. Although mutex is more powerful than Monitor, compared with the Monitor class, the interoperability conversion required by mutex consumes more computing resources. For examples of mutex usage, see Mutex.

C #
Private Mutex mutF = new Mutex (); private void ReadF () {mutF. WaitOne (); // your code to access the resource mutF. ReleaseMutex ();}

Related Article

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.