2.1 to 2.10 thread synchronization technology

  • You will learn multiple threads using conventional techniques shared resources.
    Mutex
    SemaphoreSlim
    AutoResetEvent
    manualResetSlim
    CountdownEvent
    Barrier
    ReaderWriterLockSlim
    SpinWait

  • word
Mutex 互斥
semaphoreSlim 信号灯限时。
autoResetEvent 自动重置事件
manualResetSlim 重置Slim手动
countDownEvent 倒计时事件
Barrier 障碍物
ReaderWriterLockSlim 
SpinWait 旋转等待。
  • Read: 2.5,2.6,2.8,2.10. 2.9 to be supplemented

2.1 Introduction

  • When a thread executes increment or decrement operation, other threads need to turn to wait, this common problem is referred to thread synchronization
  • If you need a shared object, then you do not perform thread synchronization. Most of the time through 重新设计程序来移除共享状态,从而去掉复杂的同步构造. If you must use a shared state, that is 使用原子操作. That is, only after the current operation is complete, other threads can perform other operations. So, you do not realize other threads waiting for the completion of the current operation, which avoids the use of locks, also ruled out a deadlock situation.
  • If the previous mode is not possible, then we have to use different ways to coordinate thread. One way is to wait for the thread in a blocked state. In this case, it will take a minimum of CPU time. However, one may introduce at least a context switch (context switch, thread scheduler), it will consume considerable resources. If the thread is to be suspended for a long time, this is worth it. This way is called "kernel mode" (the MODE-Kernel) , because only the operating system kernel threads in order to prevent the use of CPU time.
  • In case thread just have to wait a short time, it is best not to switch the thread to blocking state. While such a waste of CPU time, but to save CPU time consuming context switching. This way is called "user mode" (the User-the MODE) (This approach is very lightweight, very fast, but if the thread will have to wait a long time to waste CPU time.)
  • Future make good use of these two methods, you can use mixed mode (Hybrid) . First attempts to use mixed-mode user mode to wait, if the thread wait long enough, it will switch to a blocking state in order to save CPU resources.

Basic 2.2 atomic operation performed

  • Atomic perform basic operations on the object, and thus do not block the thread to avoid race conditions.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication3._2
{
    public class Class2_2
    {
        internal abstract class CounterBase
        {
            public abstract void Increment();
            public abstract void Decrement();

        }
        internal static void TestCounter(CounterBase c)
        {
            for (int i = 0; i < 10000; i++)
            {
                c.Increment();
                c.Decrement();
            }
        }

        internal class Counter : CounterBase
        {
            private int _count;
            public int Count { get { return _count; } }
            public override void Decrement()
            {
                _count--;
            }

            public override void Increment()
            {
                _count++;
            }
        }
        internal class CounterNoLock : CounterBase
        {
            private int _count;
            public int Count { get { return _count; } }
            public override void Decrement()
            {
                Interlocked.Decrement(ref _count);
            }

            public override void Increment()
            {
                Interlocked.Increment(ref _count);
            }
        }
    }
}
  
static void Main(string[] args)
{ 
    #region 2.2
    Console.WriteLine("Incorret counter");
    var c = new Class2_2.Counter();
    var t1 = new Thread(() => Class2_2.TestCounter(c));
    var t2 = new Thread(() => Class2_2.TestCounter(c));
    var t3 = new Thread(()=>Class2_2.TestCounter(c));
    t1.Start();
    t2.Start();
    t3.Start();
    t1.Join();
    t2.Join();
    t3.Join();
    Console.WriteLine("total count:{0}",c.Count);
    Console.WriteLine("----------");
    Console.WriteLine("correct counter");

    var c1 = new Class2_2.CounterNoLock();
    t1 = new Thread(()=>Class2_2.TestCounter(c1));
    t2 = new Thread(()=>Class2_2.TestCounter(c1));
    t3 = new Thread(()=>Class2_2.TestCounter(c1));
    t1.Start();
    t2.Start();
    t3.Start();
    t1.Join();
    t2.Join();
    t3.Join();
    Console.WriteLine("total count:{0}", c1.Count);

    Console.ReadKey(  );
    #endregion
}
  • Code reading the code in the Main method c.Countresult is random, and c1.Countby means of Interlockedclass, do not lock any object can get the right result. Interlocked provides a method of atomic basic mathematical operations Increment, Decrement and add the like.

2.3 Class Mutex

  • Mutex (mutual exclusion) is a primitive way synchronization, which only grants them exclusive access to shared resources on a thread.
//Main方法
#region 2.3
const string MutexName = "abcd";
using (var m = new Mutex(false, MutexName))
{
    if(!m.WaitOne(5000,false))
    {
        Console.WriteLine("second instance is running");
        Console.ReadLine();
        m.ReleaseMutex();
    }
    else
    {
        Console.WriteLine("running");
        Console.ReadLine();
        m.ReleaseMutex();
    }
}
#endregion
  • Mutex constructors pass false, indicating that if the mutex has been created, allowing the program to acquire the mutex. If you do not get mutex, the program displays a simple running, waiting press any key, then release the mutex and exit.
  • After the first run we compiled a console window will appear running, and none to open a second time, a second window will try to get the mutex within 5s. If the input content after the first program is closed, the program will output a second "running". If the first window will not enter the timeout, the second window can not get mutex, and outputs "second ......".
    Be sure to properly close the mutex, preferably by using code blocks to wrap the mutex object. The mode can be used in different programs simultaneous threads can be extended to a large number of usage scenarios.

2.4 SemaphoreSlim class (semaphore)

  • This class limits the number of threads simultaneously access the same resource. Semaphore is a lightweight version of the class.
static SemaphoreSlim _se = new SemaphoreSlim(4);
static void AccessDataBase(string name,int s)
{
    Console.WriteLine("{0} 等待访问数据库 ",name);
    _se.Wait();
    Console.WriteLine("{0} 被允许访问数据库",name);
    Thread.Sleep(s*1000);
    Console.WriteLine("{0} 结束掉了 ",name);
    _se.Release();
}
static void Main(string[] args)
{ 
    for (int i = 0; i <=6; i++)
    {
        string threadName = "thread" + i;
        int s = 2 + 2 * i;
        var t = new Thread(() => AccessDataBase(threadName, s));
        t.Start();
    }
}
  • working principle

    • When the main program starts, create an instance SemaphoreSlim, specifying the number of concurrent threads allowed in the constructor. main method to start six different threads, each trying to gain access to the database. But semaphore system limits the amount of four concurrent threads. When there are four gained access to the database, the other two need to wait until the completion of the work until the thread calls the release method to send a signal.
    • Here we use a mixed mode, which allows us to use the context switching without the waiting time very short case. Then semaphoreclass uses kernel mode. Generally not necessary to use it, but you can use it in the scene synchronized across applications.

2.5 Use class AutoRestEvent

  • This class implements: send notice to the parties from one thread to another thread. It can notify the thread is waiting for some event has occurred.

#region 2.5
private static AutoResetEvent _w = new AutoResetEvent(false);
private static AutoResetEvent _m = new AutoResetEvent(false);
static void Process(int s)
{
    Console.WriteLine("开始一个工作...");
    Thread.Sleep(s*1000);
    Console.WriteLine("正在工作");
    _w.Set();
    Console.WriteLine("等待主线程完成它的工作");
    _m.WaitOne();
    Console.WriteLine("开始第二个操作...");
    Thread.Sleep(s*1000);
    Console.WriteLine("工作完成");
    _w.Set();
}
#endregion
static void Main(string[] args)
{
    #region 2.5
    var t = new Thread(()=>Process(10));
    t.Start();
    Console.WriteLine("等待其他线程完成");
    _w.WaitOne();
    Console.WriteLine("第一个操作完成");
    Console.WriteLine("在主线程上执行操作");
    _w.WaitOne();
    Console.WriteLine("第二个线程完成!");
    #endregion
}
  • When the main program starts, the two set AutoRestEvent instances. Where a child thread from the main line to Cheng Faxin number, another example is the signal from the main line Cheng Xiangzai thread. We pass false to autorestEvent constructor defines the initial state of these two examples is unsigned. This means that any thread calls a waiOne method of any of these two objects will be blocked until you call the Set method. If the initial event status is true, autoRestEvent instance state is signaled (signal), if the calling thread waitOne method will be processed immediately. Then automatically changes the event status unsigned, so the method needs to be called once set an example, so that other threads in this instance call waitOne method thus continues.
    Then we have the second thread, its Colombian operations 10s will perform, and then wait for the signal sent from the second thread. The signal means that the first operation has been completed. Now the second thread signal main thread waiting. We did some additional work on the main thread, and send a signal by calling _m.Set method. And then waits for another signal from the second thread. AutoRestEvent class takes time kernel mode, the waiting time is not too long.

2.6 Class ManualRestEventSlim

  • This class transmit signals in a more flexible way between threads.
#region 2.6
static ManualResetEventSlim _m = new ManualResetEventSlim(false);
static void TravelThroughGates(string threadName,int seconds)
{
    Console.WriteLine("{0} 睡着了", threadName);
    Thread.Sleep(seconds * 1000);
    Console.WriteLine("{0} 等待开门!",threadName);
    _m.Wait();
    Console.WriteLine("{0} 进入大门!",threadName);

}
#endregion
static void Main(string[] args)
{
    #region 1.
    #endregion

    #region 2.6
    var t1 = new Thread(()=> TravelThroughGates("thread1",5));
    var t2 = new Thread(()=> TravelThroughGates("thread2",6));
    var t3 = new Thread(() => TravelThroughGates("thread3",12));
    t1.Start();
    t2.Start();
    t3.Start();
    Thread.Sleep(3000);
    Console.WriteLine("门打开了!");
    _m.Set();
    Thread.Sleep(2000);
    _m.Reset();
    Console.WriteLine("门关闭了1!");
    Thread.Sleep(10000);
    Console.WriteLine("门第二次打开了!");
    _m.Set();
    Thread.Sleep(2000);
    Console.WriteLine("门关闭了2!");
    _m.Reset();
    Console.ReadLine(  );
    #endregion
}
  • ManualRestEventSlim entire works a bit like the crowd through the door. AutoResetEvent event like a revolving door, can only one person through. ManualRestEventSlim is mixed version ManualRestEvent, the door remains open until you manually adjust the Reset method. When invoked _m.Set, the equivalent of opening the door to allow ready thread receives the signal and continue working. Thread 3 then still sleep, no time to catch up. When the call _m.Reset()is equivalent to close the door. The last thread is ready to execute, but had to wait for the next signal, that is, to wait for a few seconds.
  • EventWaitHandle class is the base class and ManualRestEvent AutoRestEvent class.

2.7 CountDownEvent class

  • This demonstrates the use of the code signal CountDownEvent class to wait until a certain number of operations is completed.
#region 2.7
static CountdownEvent _c = new CountdownEvent(2);
static void PerformOperation(string message,int s)
{
    Thread.Sleep(s*1000);
    Console.WriteLine(message);
    _c.Signal();//向CountdownEvent 注册信号,同时减少其计数。
}
#endregion
static void Main(string[] args)
{
    #region 1.
    #endregion

    #region 2.7
    Console.WriteLine("开始两个操作");
    var t1 = new Thread(()=>PerformOperation("操作1完成了",4));
    var t2 = new Thread(()=>PerformOperation("操作2完成了",8));
    t1.Start();
    t2.Start();
    _c.Wait();
    Console.WriteLine("两个操作完成了!");
    _c.Dispose();
    Console.ReadKey();
    #endregion
}
  • working principle

    • When the main program starts, creates a CountdownEvent example, the specified operation is completed when the two signals will be in its constructor. And then start two threads, when the execution is complete will send a signal. Once the second thread completes, the main thread returns from the state of waiting Countdownevent and continue. For situations need to wait for multiple asynchronous operation is complete, the use of this approach is very convenient. However, there is a major drawback. If the call _c.Signal()is not to a specific number of times, then _c.Wait()waits. Be sure to use countdownEvent, all threads should call signal after completion method.

2.8 Use Barrier class

  • This class is used to organize multiple threads meet at some point in time. It provides a callback function, each thread calls the callback function will be executed after SignalAndWait method.

* Microsoft library to explain the role: to be able to employ multiple tasks in parallel in accordance with an algorithm to work in multiple stages.
c #region 2.8 static Barrier _b = new Barrier(2, b => Console.WriteLine("end of phase {0}",b.CurrentPhaseNumber+1));//获取屏障的当前阶段的编号。 static void PlayMusic(string name,string message,int s) { for (int i = 0; i < 3; i++) { Console.WriteLine("---------"); Thread.Sleep(s*1000); Console.WriteLine("{0} starts to {1}",name,message); Thread.Sleep(s*1000); Console.WriteLine("{0} finishes to {1}",name,message); _b.SignalAndWait(); } } #endregion static void Main(string[] args) { var t1 = new Thread(()=>PlayMusic("吉他手","play an amzing solo",5)); var t2 = new Thread(()=> PlayMusic("歌手 ","sing his song",2)); t1.Start(); t2.Start(); Console.Read(); } #endregion

  • working principle

    • Creating a Barrier class that specifies that we want to synchronize the two threads. In the two threads in any one call _b.SignalAndWaitafter the method executes a callback function to print out phase.
      Each thread will send two signals barrier, so there will be two stages. Every time these two threads call signalAndWait methods, barrier will execute the callback function. This is useful in multi-threaded iteration, you can perform some calculations before the end of each iteration. You can interact at the end of the iteration when the last thread calls signalandwait method.

2.10 Using SpinWait class

  • How not to use the model to make the kernel thread to wait. In addition, he was a hybrid synchronous SpinWait structure, it is designed to use user-mode to wait for some time, then switch to kernel mode in order to save CPU time.
    * Microsoft libraries Description: SpinWait provide support for spin-based waiting.
 #region 2.10
static volatile bool _isOver = false;
static void UserModeWait()
{
    while (!_isOver)
    {
        Console.WriteLine(".");
    }
    Console.WriteLine();
    Console.WriteLine("waiting is complete");
}
static void HybridSpinWait()
{
    var w = new SpinWait();
    while (!_isOver)
    {
        w.SpinOnce();
        Console.WriteLine(w.NextSpinWillYield);
    }
    Console.WriteLine("waiting is complete");
}
#endregion
static void Main(string[] args)
{ 
    #region 2.10
    var t1 = new Thread(UserModeWait);
    var t2 = new Thread(HybridSpinWait);
    Console.WriteLine("运行用户模式等待");
    t1.Start();
    Thread.Sleep(20);
    _isOver = true;
    Thread.Sleep(1000);
    _isOver = false;
    Console.WriteLine("运行混合自旋等待结构等待");
    t2.Start();
    Thread.Sleep(5);
    _isOver = true;
    Console.Read();
    #endregion
}
  • When the main program starts, defines a thread, an endless loop will execute until the rear main thread 20 milliseconds _isOver variable set to true. We can run the test period is 20 ~ 30s, measuring CPU load through the Task Manager. Depending on the number of CPU cores, the Task Manager will show a significant processing time.
    Use the volatile keyword to declare _isOver static fields, the keyword field may indicate a return to modify multiple threads are executed simultaneously. Declared as volatile field will not be optimized compiler and processor can only be accessed by a single thread. This ensures that the field is the latest value.
    Then use spinwait version for print to be displayed on each iteration a special flag thread is switched to the blocked state. Run the thread 5 milliseconds see the results. At first, spinwait try to use the user mode, after nine iterations, begin switching threads blocked. If you try to measure the version of the CPU load, the win will not see the Task Manager task CPU usage.

Guess you like

Origin www.cnblogs.com/anjun-xy/p/11832743.html
Recommended