V. C # multithreading study the use of the Mutex class for synchronization methods and their differences with the Monitor class and Lock class

Source: Internet
Author: User

The Framework provides three locking mechanisms: the Monitor class, the Lock key word, and the Mutex class.

  In general, lock and monitor can lock objects or functions, while mutex is generally used to lock functions and ensure that functions are synchronously called between different threads, without being affected by the thread priority. When the lock and monitor objects are used to lock an object (that is, the object is locked outside the object, or the object is locked where the function in the object is called), the object can be accessed by only one thread at a time, the premise is that the objects locked by multiple threads must be the same object. Therefore, a global object should be defined in this case. When a function is locked, the function inside the object is locked, this function can also be accessed by only one thread at a time, provided that each thread accesses the function of the same object. No matter whether the lock and monitor lock objects or functions, functions cannot be synchronized between different threads, that is, functions are not accessed by different threads in sequence, A thread with a higher priority will occupy this object or the function in the object all the time. After a while, the thread will be "occupied" by other threads. Only the lock and mutex will be used together, in order to invalidate the function priority and ensure the synchronization of methods. Mutex objects are usually locked inside the function, rather than in the place where the function is called.

  To sum up, the similarities and differences between the three locks are as follows:

  (1) monitor locks the function caller. mutex locks the function internally, that is, it locks the called end. The lock is between the two, and the caller and the called party can eat the lock;

  (2) When using monitor and lock to lock the caller, you must ensure that the objects accessed by different threads are the same object; otherwise, they are not locked at all;

  (3) monitor and lock ensure that only one thread can access the locked part at a time, but synchronization is not guaranteed. mutex can ensure that the locked object can be accessed synchronously by different threads.

  The following example demonstrates the differences between the three objects.

Monitor and Lock are mostly used to Lock the called end, while Mutex is used to Lock the called end. For example, the following program: because such programs are within milliseconds, running the following program may have different results on different machines, running on the same machine at different times has different results. My testing environment is vs2005, windowsXp, CPU3.0, and 1G monery. There are two threads thread1, thread2, and a TestFunc function in the program. TestFunc prints the thread name for calling it and the call time (mm-level ), the two threads call the function TestFunc with 30mm and 100mm respectively. The execution time of TestFunc is 50mm. The procedure is as follows:
Using System;
Using System. Collections. Generic;
Using System. Text;
Using System. Threading;
Namespace MonitorLockMutex
{
Class Program
{
# Region variable
Thread thread1 = null;
Thread thread2 = null;
Mutex mutex = null;
# Endregion
Static void Main (string [] args)
{
Program p = new Program ();
P. RunThread ();
Console. ReadLine ();
}
Public Program ()
{
Mutex = new Mutex ();
Thread1 = new Thread (new ThreadStart (thread1Func ));
Thread2 = new Thread (new ThreadStart (thread2Func ));
}
Public void RunThread ()
{
Thread1.Start ();
Thread2.Start ();
}
Private void thread1Func ()
{
For (int count = 0; count <10; count ++)
{
TestFunc ("Thread1 have run" + count. ToString () + "times ");
Thread. Sleep (30 );
}
}
Private void thread2Func ()
{
For (int count = 0; count <10; count ++)
{
TestFunc ("Thread2 have run" + count. ToString () + "times ");
Thread. Sleep (100 );
}
}
Private void TestFunc (string str)
{
Console. WriteLine ("{0} {1}", str, System. DateTime. Now. Millisecond. ToString ());
Thread. Sleep (50 );
}
}
}
The running result is as follows: if no lock is applied, the two threads read the TestFunc function according to their respective time interval + TestFunc execution time (50mm. Because the thread needs to allocate memory at the beginning, 0th calls are inaccurate, from 1st ~ The execution interval of thread1 is about 80mm, and the execution interval of thread2 is about 150mm.

Modify TestFunc as follows:
Private void TestFunc (string str)
{
Lock (this)
{
Console. WriteLine ("{0} {1}", str, System. DateTime. Now. Millisecond. ToString ());
Thread. Sleep (50 );
}
}
Or the same is true with Monitor, as shown below:
Private void TestFunc (string str)
{
Monitor. Enter (this );
Console. WriteLine ("{0} {1}", str, System. DateTime. Now. Millisecond. ToString ());
Thread. Sleep (50 );
Monitor. Exit (this );
}
Both Enter and Exit are static methods in Monitor. The result of running Lock is as follows: Let's analyze the result, which also starts from 1st times. The call interval between the same threads is the thread execution time + TestFunc call time. The call interval between different threads is the TestFunc call time. For example, the interval between two consecutive calls of thread1 is about 30 + 50 = 80; the interval between two consecutive calls of thread2 is about 100 + 50 = 150. The interval between calling thread1 and thread2 is 50mm. Because TestFunc is locked, when TestFunc is called by a thread and other threads call TestFunc at the same time, the later threads will be queued to the waiting queue to wait, until the thread with access permission releases the resource.

This is the feature of locking the called function, that is, only one thread can call each time, the number of calls with a higher thread priority is more, and the number of calls with a lower thread priority is less. This is the so-called strong proportion. Next let's take a look at the usage of the Mutex class, and the difference with Monitor and Lock. Modify the Code as follows:
Private void thread1Func ()
{
For (int count = 0; count <10; count ++)
{
Mutex. WaitOne ();
TestFunc ("Thread1 have run" + count. ToString () + "times ");
Mutex. ReleaseMutex ();
}
}

Private void thread2Func ()
{
For (int count = 0; count <10; count ++)
{
Mutex. WaitOne ();
TestFunc ("Thread2 have run" + count. ToString () + "times ");
Mutex. ReleaseMutex ();
}
}

Private void TestFunc (string str)
{
Console. WriteLine ("{0} {1}", str, System. DateTime. Now. Millisecond. ToString ());
Thread. Sleep (50 );
}

The running result is similar to the preceding one. It can be seen that Mutex can only be called between mutually exclusive threads, but it cannot be used repeatedly in this thread. That is, waitOne () in thread1 only acts mutually exclusive to waitOne () in thread2, however, thread1 is not affected by this wainOne (). It can be called multiple times, but the same number of ReleaseMutex () can be called after the call ends.

So how can threads be executed in sequence according to the call sequence? In fact, you can use the lock and Mutex together. Now add another thread and change the Code as follows:

Using System;
Using System. Collections. Generic;
Using System. Text;

Using System. Threading;

Namespace ThreadLock
{
Class Program
{
# Region variable
Thread thread1 = null;
Thread thread2 = null;
Thread thread3 = null;
Mutex mutex = null;
# Endregion
Static void Main (string [] args)
{
Program p = new Program ();
P. RunThread ();
Console. ReadLine ();
}
Public Program ()
{
Mutex = new Mutex ();
Thread1 = new Thread (new ThreadStart (thread1Func ));
Thread2 = new Thread (new ThreadStart (thread2Func ));
Thread3 = new Thread (new ThreadStart (thread3Func ));
}
Public void RunThread ()
{
Thread1.Start ();
Thread2.Start ();
Thread3.Start ();
}
Private void thread1Func ()
{
For (int count = 0; count <10; count ++)
{
Lock (this)
{
Mutex. WaitOne ();
TestFunc ("Thread1 have run" + count. ToString () + "times ");
Thread. Sleep (30 );
Mutex. ReleaseMutex ();
}
}
}
Private void thread2Func ()
{
For (int count = 0; count <10; count ++)
{
Lock (this)
{
Mutex. WaitOne ();
TestFunc ("Thread2 have run" + count. ToString () + "times ");
Thread. Sleep (100 );
Mutex. ReleaseMutex ();
}
}
}
Private void thread3Func ()
{
For (int count = 0; count <10; count ++)
{
Lock (this)
{
Mutex. WaitOne ();
TestFunc ("Thread3 have run" + count. ToString () + "times ");
Thread. Sleep (100 );
Mutex. ReleaseMutex ();
}
}
}
Private void TestFunc (string str)
{
Console. WriteLine ("{0} {1}", str, System. DateTime. Now. Millisecond. ToString ());
Thread. Sleep (50 );
}

}
}

The running result is as follows:

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.