1. Thread synchronization within the process
1. Use lock, the usage is as follows:
private static readonly object SeqLock = new object(); private void Print() { lock (SeqLock) { ("test"); } }
Feature: Only objects can be passed, and the waiting timeout cannot be set
2. Use: InterLocked (atomic operation)
In the namespace, Interlocked is actually a class control counter, thereby realizing the synchronization of processes, which is easy to implement the producer consumer model.
//The buffer can only hold one character private static char buffer; //Identification amount (the space used in the buffer, the initial value is 0) private static long numberOfUsedSpace = 0; static void Main(string[] args) { //Thread: Writer Thread Writer = new Thread(delegate () { string str = "The words here will be read one by one, and no one will be missing,"; for (int i = 0; i < 24; i++) { //Check whether the buffer is full before writing data //If it is full, wait until the data in the buffer is read by the process Reader while ((ref numberOfUsedSpace) == 1) { (50); } buffer = str[i]; //Write data to the buffer //After writing data, mark the buffer as full (changed from 0 to 1) (ref numberOfUsedSpace); } }); //Thread: Reader Thread Reader = new Thread(delegate () { for (int i = 0; i < 24; i++) { //Check whether the buffer is empty before reading data //If it is empty, wait until the process Writer writes data into the buffer while ((ref numberOfUsedSpace) == 0) { (50); } char ch = buffer; //Read data from the buffer (ch); (ref numberOfUsedSpace); } }); //Start the thread (); (); ();
3. Use Monitor
Where () and lock are the same
(obj){ //Synchronized part }finally{ (obj); }
TryEnter can set the waiting time, etc.
bool lockTaken=false; (obj, 500, ref lockTaken); if(lockTaken){ try { //Synchronized part } finally { (obj); } }else{ //don't aquire the lock, excute other parts }
2. Synchronization between processes
1. WaitHandle:
Encapsulates an operating system-specific object waiting for exclusive access to shared resources. WaitHandle: It is an abstract class. We generally do not use it directly, but use its derived class:
AutoResetEvent、EventWaitHandle、ManualResetEvent、Mutex、Semaphore
The method of this abstract class is as follows:
WaitOne(): Wait for the occurrence of a signal and set the timeout;
WaitAll(): Wait for multiple signals to appear, and the timeout can be set;
WaitAny(): WaitAny(): Wait for any signal to appear, and the timeout can be set;
2. Mutex: Similar to Monitor, only one thread can acquire the lock. Use WaitOne() to obtain the lock and use ReleaseMutex() to unlock it. The constructor is used as follows:
bool isNew = false; mutex = new Mutex(false, "Mutex1", out isNew);
Parameter 1: Whether the lock is owned by the main thread after it is created. If set to true, it is equivalent to calling WaitOne() and needs to be released, otherwise other threads cannot acquire the lock;
Parameter 2: Lock name, existing locks can be opened through OpenExist() or TryOpenExist(). Because the operating system recognizes interlocks with names, they can be shared by different processes. If the lock name is empty, it is an unnamed interlock and cannot be shared between multiple processes;
Parameter 3: Whether it is a newly created interlock;
The following example demonstrates the use of Mutex between processes: class Program
private static Mutex mutex = null; static void Main(string[] args) { bool isNew = false; mutex = new Mutex(false, "Mutex1", out isNew); ("Main Start...."); (); ("Aquire Lock and Running...."); (10000); (); ("Release Lock...."); ("Main end...."); (); } }
Run the exe of this console program twice in a row. The result is as follows. The first one runs to acquire Mutex1 interlock, and the next one will wait until the previous one releases Mutex1 interlock.
: The semaphore acts similar to the mutex lock, but it can define a certain number of threads to use at the same time. Here is the constructor:
bool isNew = false; semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
Parameter 1: After creation, if the number of locks released initially, if parameter 1 is set to 2 and parameter 2 is set to 3, then only 2 locks are available after creation, and the other one has been locked;
Parameter 2: Define the number of available locks;
Parameter 3: The name of the semaphore, similar to Mutex;
Parameter 4: Whether it is a newly created interlock;
The following example creates the semaphore "semaphore1". Use() to run Func1() synchronously. In Func1(), when a thread acquires a semaphore lock, releases the lock or waits for a timeout, it will be output in the console.
class Program { private static Semaphore semaphore = null; static void Main(string[] args) { ("Main Start...."); bool isNew = false; semaphore = new Semaphore(3, 3, "semaphore1", out isNew); (0, 6, Func1); ("Main end...."); (); } static void Func1(int index) { ("Task {0} Start....",); bool isComplete = false; while (!isComplete) { if ((1000)) { try { ("Task {0} aquire lock....", ); (5000); } finally { (); ("Task {0} release lock....", ); isComplete = true; } } else { ("Task {0} timeout....", ); } } }
The running result is as follows: threads 1, 2, 3 first acquire the semaphore lock, threads 4, 5, 6 are waiting until 1, 2, 3 is released,
4. AutoResetEvent class:
You can use events to notify other tasks, and the constructor is public AutoResetEvent(bool initialState).
When initialState=true, it is in signaled mode (terminated state), calling waitone() will not block the task, wait for signals, and call the Reset() method, which can be set to non-signaled mode;
When initialState=fasle is in non-signaled mode (non-termined state), calling waitone() will wait for the signal to block the current thread (can be called in multiple threads and block multiple threads at the same time) until the call set() sends the signal and releases the thread (called once, only one thread can be released). This method is generally used;
The following example creates 5 tasks, calls waitone() to block the thread, and then calls set() every 2 seconds.
private static AutoResetEvent autoReset = new AutoResetEvent(false); static void Main(string[] args) { ("Main Start...."); for (int i = 0; i < 5; i++) { (() => { ("{0} Start....", ); (); ("{0} Continue....", ); }); } for (int i = 0; i < 5;i++ ) { (2000); (); } ("Main end...."); (); }
The order of each run is slightly different, and the release is random:
5. ManualResetEvent class: The function is basically similar to AutoSetEvent, but another difference:
Using AutoSetEvent, each time you call set() and switch to the termination mode, you can only release one waitone(), and it will automatically switch to the non-termination mode; but ManualResetEvent, call set() and switch to the termination mode, you can release all the current waitone(), and you need to call reset() manually to switch to the non-termination mode.
The following example illustrates this difference:
private static ManualResetEvent manualReset = new ManualResetEvent(false); static void Main(string[] args) { ("Main Start...."); for (int i = 0; i < 5; i++) { (() => { ("{0} Start....", ); (); ("{0} Continue....", ); }); } (2000); (); (); ("it doesn't work now, Main continue...."); (); (); ("Main end...."); (); }
The above is the detailed content of the C# thread synchronization method. For more information about C# thread synchronization, please follow my other related articles!