As you learn more about multithreading, you may find it necessary to understand some of the issues associated with thread-sharing resources. The. NET Framework provides many classes and data types to control access to shared resources.
Consider a situation we often encounter: there are global variables and shared class variables, we need to update them from different threads, and we can do this by using the System.Threading.Interlocked class, which provides an atomic, modularized integer update operation.
There is also a section of code that you can use to lock objects by using the System.Threading.Monitor class so that it cannot be accessed by another thread for the time being.
An instance of the System.Threading.WaitHandle class can be used to encapsulate an operating system-specific object that waits for exclusive access to a shared resource. In particular, interoperability issues with non-regulated code.
System.Threading.Mutex is used for synchronization of multiple complex threads, and it also allows for single-threaded access.
Synchronization event classes such as ManualResetEvent and AutoResetEvent support a class that notifies other events of the thread.
Not discussing thread synchronization issues equals little knowledge of multithreaded programming, but we need to be very cautious about using multithreaded synchronization. In the use of thread synchronization, we need to be able to correctly determine that the object and method is likely to cause deadlock (deadlock is all the threads have stopped the corresponding, are waiting for each other to release resources). And the problem with the stolen data (which is inconsistent with the data being manipulated by multiple threads at the same time), this is not easy to understand, so to speak, there are X and y two threads, thread x reads data from the file and writes data to the structure, and thread y reads the data from that data structure and sends it to other computers. Assuming that X writes data while y reads the data, it is clear that the data Y reads is inconsistent with the actual stored data. This is obviously something we should avoid. A small number of threads will make the problem more likely to occur, and access to shared resources is better synchronized.
The. NET framework's CLR provides three methods for accomplishing shared resources, such as global variable domains, specific code snippets, static and instantiated methods, and domains.
(1) Code domain synchronization: Use the Monitor class to synchronize static/instantiated methods of all code or part of the code snippet. Synchronization of static domains is not supported. In the instantiated method, the this pointer is used for synchronization, and in a static method, the class is used for synchronization, which is described later.
(2) Manual synchronization: Create your own synchronization mechanisms using different synchronization classes (such as WaitHandle, mutexes, ReaderWriterLock, ManualResetEvent, AutoResetEvent, and interlocked). This synchronization requires you to manually synchronize different domains and methods, and this synchronization can also be used for synchronization between processes and deadlock cancellation for shared resource waiting.
(3) Context synchronization: Use SynchronizationAttribute to create a simple, automatic synchronization for the ContextBoundObject object. This synchronization is used only for instantiated methods and synchronization of domains. All objects in the same context domain share the same lock.
Monitor Class
The Monitor class is ideal for thread synchronization in this case, at a given time and the specified code snippet can only be accessed by one thread. The methods in this class are static, so there is no need to instantiate this class. Some of the following static methods provide a mechanism to synchronize the access of objects to avoid deadlocks and maintain data consistency.
Monitor.Enter method: Gets an exclusive lock on the specified object.
Monitor.TryEnter method: Attempts to obtain an exclusive lock on the specified object.
Monitor.Exit method: Frees exclusive locks on the specified object.
Monitor.Wait method: Frees the lock on the object and blocks the current thread until it retrieves the lock again.
Monitor.pulse method: Notifies a thread in the waiting queue to lock changes to the object state.
Monitor.pulseall method: Notifies all waiting thread object state changes.
Access to a code snippet can be synchronized by locking and unlocking the specified object. Monitor.Enter, Monitor.TryEnter, and monitor.exit are used to lock and unlock the specified object. Once the lock of the specified object (code snippet) is fetched (called Monitor.Enter), no other thread can acquire the lock. For example, thread x obtains an object lock that can be freed (call Monitor.Exit (object) or monitor.wait). When this object lock is freed, the Monitor.pulse method and the Monitor.pulseall method notify the next thread of the ready queue and the threads of all other ready queues will have the opportunity to acquire an exclusive lock. Thread x frees the lock while thread y obtains the lock, and calls the monitor.wait thread x into the wait queue. When the thread (thread y) from the currently locked object is pulse or PulseAll, the thread waiting for the queue enters the ready queue. Monitor.Wait returns when thread X gets the object lock back. If the thread (thread y) that owns the lock does not invoke pulse or PulseAll, the method may be locked in an indeterminate way. Pulse, PulseAll and wait must be synchronized code segments of the jaw are invoked. For each synchronized object, you need a pointer to a thread that currently has a lock, a ready queue, and a wait queue that contains a thread that needs to be notified of the state change of the locked object.
You might ask what happens when two threads call Monitor.Enter at the same time? No matter how close the two threads call Monitor.Enter, there is definitely one in the front, one in the back, so there will always be a lock to get the object. Since Monitor.Enter is an atomic operation, it is not possible for the CPU to prefer a thread to another thread. For better performance, you should delay the acquisition lock invocation of the latter thread and immediately release the object lock of the previous thread. Lock is feasible for private and internal objects, but it can cause deadlocks for external objects because unrelated code may lock the same object for different purposes.
If you want to lock a piece of code, the best thing to do is to add a statement that sets the lock in the try statement and place the Monitor.Exit in the finally statement. For an entire code snippet lock, you can use the MethodImplAttribute (in the System.Runtime.CompilerServices namespace) class to set the synchronization value in its constructor. This is an alternative method, and when the lock method returns, the lock is released. If you need to release the lock quickly, you can replace the above method with the Monitor class and the C # Lock declaration.
Let's look at a section of code that uses the Monitor class:
public void Some_method ()
{
int a=100;
int b=0;
Monitor.Enter (this);
Say we do something here.
int c=a/b;
Monitor.Exit (this);
}
The code above will cause problems. When the code runs to int c=a/b; , an exception will be thrown and Monitor.Exit will not return. So this program will hang, and the other threads will not get the lock. There are two ways to solve the problem above. The first method is to put the code inside the try...finally and finally call Monitor.Exit, which will eventually release the lock. The second approach is to take advantage of the lock () method of C #. Calling this method and invoking the Monitoy.enter effect is the same. However, once the code execution is out of scope, releasing the lock will not occur automatically. See the following code:
public void Some_method ()
{
int a=100;
int b=0;
Lock (this);
Say we do something here.
int c=a/b;
}
The C # Lock declaration provides the same functionality as Monitoy.enter and Monitoy.exit, which is used in situations where your code snippets cannot be interrupted by other independent threads.
WaitHandle Class
The WaitHandle class is used as a base class, and it allows multiple wait operations. This class encapsulates the Win32 synchronous processing method. The WaitHandle object notifies other threads that it requires exclusive access to resources, and other threads must wait until WaitHandle no longer uses the resource and wait handles are not used. Here are a few of the classes inherited from it:
Mutex classes: Synchronization Primitives can also be used for interprocess synchronization.
AutoResetEvent: Notifies one or more waiting threads that an event has occurred. This class cannot be inherited.
ManualResetEvent: Occurs when you notify one or more waiting thread events that have occurred. This class cannot be inherited.
These classes define a number of signaling mechanisms that allow for exclusive access to resources for possession and release. They have two states: signaled and nonsignaled. The wait handle for the signaled state does not belong to any thread unless it is a nonsignaled state. If the thread that owns the wait handle is no longer using the Set method when the wait handle is used, other threads can call the Reset method to change the state or any WaitHandle method requires a wait handle, which is shown below:
WaitAll: Waits for all elements in the specified array to receive a signal.
WaitAny: Waits for any element in the specified array to receive a signal.
WaitOne: When overridden in a derived class, blocks the current thread until the current WaitHandle receives a signal.
These wait methods block threads until one or more of the synchronization objects receive a signal.
The WaitHandle object encapsulates an operating system-specific object that waits for exclusive access to a shared resource, either Hing code or unmanaged code. But it's not portable, monitor is fully managed code and it's very efficient for operating system resources.
Mutex Class
A mutex is another method of synchronizing between threads and across processes, and it also provides synchronization between processes. It allows one thread to monopolize the sharing of resources while preventing access to other threads and processes. The name of the mutex is a good illustration of its owner's exclusive possession of the resource. Once a thread has a mutex, the other threads that want the mutex are suspended until the owning thread frees it. The Mutex.releasemutex method is used to free a mutex, a thread can call the wait method multiple times to request the same mutex, but the same number of Mutex.releasemutex must be invoked when the mutex is freed. If no thread occupies a mutex, then the state of the mutex becomes signaled, otherwise it is nosignaled. Once the state of the mutex becomes signaled, the next thread waiting for the queue will get the mutex. The mutex class corresponds to the CreateMutex of the Win32, and the method of creating the mutex object is very simple, and the following methods are commonly used:
A thread can gain ownership of a mutex by invoking WaitHandle.WaitOne or WaitHandle.WaitAny or WaitHandle.WaitAll. If the mutex does not belong to any thread, the above call will cause the thread to have a mutex and WaitOne will return immediately. But if there are other threads owning mutex,waitone will be trapped indefinitely waiting until the mutex is acquired. You can avoid waiting for a mutex indefinitely by specifying the parameter as the time to wait in the WaitOne method. Calling close acting on the mutex will release the owning. Once the mutex is created, you can use the GetHandle method to obtain the handle of the mutex and give it to the WaitHandle.WaitAny or WaitHandle.WaitAll method.
Here is an example:
public void Some_method ()
{
int a=100;
int b=20;
Mutex Firstmutex = new Mutex (false);
Firstmutex.waitone ();
Some kind of processing can is done here.
Int x=a/b;
Firstmutex.close ();
}
In the above example, the thread creates a mutex, but the beginning does not declare that it has it, and it owns the mutex by invoking the WaitOne method.
Synchronization Events
Synchronization times are some wait handles used to inform other threads what is going on and what resources are available. They have two states: signaled and nonsignaled. AutoResetEvent and ManualResetEvent are the synchronization events.
AutoResetEvent Class
This class can notify one or more threads that an event occurs. When a waiting thread is freed, it converts the state to signaled. Use the Set method to change its instance state to signaled. But once the waiting thread becomes signaled, its turntable automatically becomes nonsignaled. If there is no thread to listen for events, the turntable will remain signaled. This class cannot be inherited.
ManualResetEvent Class
This class is also used to notify one or more threads that an event has occurred. Its state can be set and reset manually. The manual reset time remains signaled until Manualresetevent.reset sets its state to nonsignaled, or remains nonsignaled until Manualresetevent.set sets its status to signaled. This class cannot be inherited.
Interlocked Class
It provides synchronization of variable access shared between threads, operates atomically, and is shared by threads. You can add or reduce shared variables by interlocked.increment or interlocked.decrement. It's a bit of an atomic operation, That is, these methods can represent an integer parameter increment and return a new value, and all operations are one step. You can also use it to specify the value of a variable or to check whether two variables are equal, and if they are equal, the value of one of the variables is substituted with the value specified.
ReaderWriterLock class
It defines a lock that provides a unique write/read-only mechanism that synchronizes reading and writing. Any number of threads can read data, and data locks will be needed when the data is updated by a thread. Read threads can acquire locks when and only if there are no threads written here. Write threads can get locks when there are no read threads and other write threads. Once Writer-lock is requested, all read threads will not be able to read the data until the write thread has finished accessing it. It supports pausing to avoid deadlocks. It also supports nested read/write Locks. The method of supporting nested read locks is Readerwriterlock.acquirereaderlock, If a line Chengyu write a lock, the thread pauses;
The way to support nested write locks is readerwriterlock.acquirewriterlock, The thread pauses if a line Chengyu a read lock. If there is read lock will be easy to deadlock. The safe approach is to use the Readerwriterlock.upgradetowriterlock method, This will enable the reader to be upgraded to the writer. You can use the Readerwriterlock.downgradefromwriterlock method to demote the writer to the reader. Calling Readerwriterlock.releaselock will release the lock, Readerwriterlock.restorelock will reload the lock status until the Readerwriterlock.releaselock is called.
Conclusion:
It's part of the story. NET platform, the problem of thread synchronization. In the next series of articles I will give some examples to further illustrate the use of these methods and techniques. Although the use of thread synchronization can bring great value to our programs, we'd better be able to use these methods with care. Otherwise, it's not a benefit, It's going to be a performance drop and even a program crash. Only a great deal of contact and experience will enable you to harness these skills. Minimize the use of blocks that are not complete or indeterminate in the synchronized code block, especially I/O operations; Use local variables as much as possible instead of global variables; Synchronization is used where portions of the code are shared by multiple threads and process access and status by different processes; Arrange your code so that each data is Chengri in a single line; it is not safe to share code between threads; In the next article we will learn about thread pooling.
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.