C# lock, monitor, Mutex study notes

Thread: A thread is an independent execution unit of a process. Each process has a main thread, except for the main thread, which can contain other threads.

The significance of multithreading: Multithreading helps to improve the overall responsiveness of the program and increase the efficiency of the CPU.

Multi-threaded application domain is quite unstable, because multiple threads can run shared functional modules at the same time. In order to protect the resources of the application from being destroyed, three locking mechanisms are provided for multi-threaded programs: Monitor class, Lock keyword and Mutex class.

1. lock

The function implemented by the lock is: the thread that enters later will not interrupt the current thread, but will wait for the end of the current thread to continue execution.

application:

 private Object thisLock=new object();
   lock(thisLock){
               //锁定的代码块
    }

Note: Avoid locking the public type, otherwise the instance will be beyond the control of the code.

The common structures lock (this), lock (typeof (MyType)), and lock ("myLock")
violate this guideline: if the instance is publicly accessible, there will be a lock (this) problem.
If MyType is publicly accessible, a lock (typeof (MyType)) problem will occur.
Since any other code in the process that uses the same string will share the same lock, the lock("myLock") problem occurs.
The best practice is to define private objects to lock, or private static object variables to protect data shared by all instances.
The following example illustrates the application of lock: In the
following example, 5 secondary threads are created, and the tasks completed by the secondary threads are: output the thread code, delay 1 second, and then output the time at that time

Example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication5 {
    class Program {
        static void Main(string[] args) {
        Console.WriteLine ("程序开始时间:"+DateTime.Now .ToString());
        Example ex=new Example ();
        Thread []threads=new  Thread[5];
        for (int i=0;i<5;i++)
        {
        threads[i]=new Thread (new ThreadStart(ex.OutPut));
        threads[i].Name =string.Format ("Worker thread#{0}",i) ;
        }
        foreach(Thread t in threads){
        t.Start();
        }
        Console.WriteLine("主线程最后一句代码!"+DateTime.Now.ToString());
        }
    }
    class Example{
    private static object  thisLock=new object ();
    public void OutPut()
    {    
    lock(thisLock){
    Console .WriteLine("->{0}",Thread.CurrentThread.Name);
    Thread.Sleep(1000);
    Console.WriteLine(DateTime.Now);
    }
    }
    
    }
}

The experimental results are as follows:

           Figure 1: Add lock

      Figure 2: No lock    

It can be seen from the experimental results that after lock is added, the program can only execute one thread at a time. Only when the current thread is executed, the next thread will be executed without lock. The program execution is chaotic and easy to block.

2. Monitor

The lock is an encapsulation of the Enter and Exit of the Monitor, so the combination of the Enter() and Exit() methods of the Monitor class can be replaced by the lock keyword.

In addition to the lock function, the Monitor class also has the following functions:

TryEnter() solves the problem of long-term death. If a concurrency occurs frequently and lasts for a long time, using TryEnter can effectively prevent deadlock or long waiting.    

Wait() releases the lock on the object to allow other threads to lock and access the object. When other threads access the object, the calling thread will wait. Pulse signals are used to notify waiting threads of changes in the state of objects.

Pulse(), PulseAll() send signals to one or more waiting threads. This signal informs the waiting thread that the state of the locked object has changed and that the owner of the lock is ready to release the lock. The waiting thread is placed in the object's ready queue so that it can finally receive the object lock. Once the thread has the lock, it can check the new state of the object to see if it has reached the desired state.

Note: The Pulse, PulseAll, and Wait methods must be called from within a synchronized code block.

3. Mutex (mutual exclusion)

The outstanding feature of Mutex is that it can perform exclusive access to resources across application domain boundaries, that is, it can be used to synchronize threads in different processes. This function is at the expense of more system resources.    

Mutex and event object EventWaitHandler belong to the kernel object. The kernel object is used for thread synchronization. The thread must switch between user mode and kernel mode, so the efficiency is generally very low. However, the use of mutually exclusive objects and event objects such as kernel objects, It is possible to synchronize between threads in multiple processes.

The mutex Mutex is similar to a baton. The thread that receives the baton can start running. Of course, the baton belongs to only one thread at a time (Thread Affinity). If this thread does not release the baton (Mutex.ReleaseMutex), then all other things that need the baton to run The thread can only wait to see the excitement.


Guess you like

Origin blog.csdn.net/youarenotme/article/details/77877882