当前位置: 代码迷 >> 综合 >> C# 多线程同步方法 之 Monitor.Wait 与 Monitor.Pulse
  详细解决方案

C# 多线程同步方法 之 Monitor.Wait 与 Monitor.Pulse

热度:42   发布时间:2023-12-10 17:49:31.0

1、线程同步原理

组成:(以单个object为准)
    1、[持锁线程]    = locker             = 持锁中
    2、[就绪队列]    = ready queue   = 一等队列,队列中的线程将被挂起等待,系统会自动按顺序为线程分配对象的排他锁
    3、[等待队列]    = wait queue      = 二等队列,队列中的线程将被挂起等待,需要通过Pulse来控制向前

2、Monitor 类使用说明

3、测试例子 A

模拟效果:

       //线程A1  lock (locker){//线程B               //线程C2                     lock (locker)         lock (locker){                     {3     Monitor.Wait                              4                        Monitor.Pulse          5                     }                         6                                           }7  }                                        ----------------【第1步】 当前锁为闲置状态则可直接获得锁持锁线程:A就绪队列:等待队列:----------------【第2步】 此时A线程已持锁,那BC则按顺序进入[就绪队列]排队持锁线程:A就绪队列:BC等待队列:----------------【第3步】 A线程主动释放锁并转入[等待队列],此时的锁将腾出来给[就绪队列]中的第一个成员使用持锁线程:B就绪队列:C等待队列:A----------------【第4步】 B线程发出指令,将[等待队列]中的第一个成员插入到[就绪队列]的第二位持锁线程:B就绪队列:CA等待队列:----------------【第5步】 B线程结束自动释放锁,此时的锁将腾出来给[就绪队列]中的第一个成员使用持锁线程:C就绪队列:A等待队列:----------------【第6步】 C线程结束自动释放锁,此时的锁将腾出来给[就绪队列]中的第一个成员使用持锁线程:A就绪队列:等待队列:----------------【第7步】 A线程结束自动释放锁,此时的锁将腾出来给[就绪队列]中的第一个成员使用持锁线程:就绪队列:等待队列:

测试代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace ConsoleApplication1
{public class Class1{public static object locker = new object();public static Stopwatch stopwatch = new Stopwatch();public static int index = 1;public static int start = 0;public static void Main(string[] args){stopwatch.Start();var tasks = new Task[]{Task.Run(delegate { A("A");}),Task.Run(delegate { B("B");}),Task.Run(delegate { C("C");}),};while (Console.ReadKey().Key == ConsoleKey.Enter){start++;Console.WriteLine();Console.WriteLine($"【第{start}步】");}}public static void ConsoleWriteLine(string text) => Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} {stopwatch.ElapsedMilliseconds.ToString().PadLeft(5, ' ')}\t{index++.ToString().PadLeft(2, ' ')}\t{text}");public static void A(string tag){SpinWait.SpinUntil(() => start >= 1);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 3);ConsoleWriteLine($"{tag}\t[进入等待队列]");if (Monitor.Wait(locker))ConsoleWriteLine($"{tag}\t[重新获得锁]");elseConsoleWriteLine($"{tag}\t[重新获得锁(超时)]");SpinWait.SpinUntil(() => start >= 7);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}public static void B(string tag){SpinWait.SpinUntil(() => start >= 2);Thread.Sleep(00);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 4);ConsoleWriteLine($"{tag}\t[Pulse]");Monitor.Pulse(locker);SpinWait.SpinUntil(() => start >= 5);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}public static void C(string tag){SpinWait.SpinUntil(() => start >= 2);Thread.Sleep(10);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 6);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}}
}

测试结果:

4、测试例子 B

模拟效果:

       //线程A                //线程B                //线程C       1  lock (locker)          lock (locker)         lock (locker)  {                      {                     {              2     Monitor.Wait(1000)     Monitor.Wait          //线程D                 //线程Elock (locker)           lock (locker){                       {3                                                  Monitor.PulseAll4                                               }5  }                                                                  6                         }                                                                   }7                         8                                                                     }----------------【第1步】持锁线程:A就绪队列:BC等待队列:----------------【第2步】持锁线程:C就绪队列:DE等待队列:AB----------------【第3步】 Pulse会将[等待队列]的成员插入到[就绪队列]的第二位持锁线程:C就绪队列:DABE等待队列:----------------【第4步】持锁线程:D就绪队列:ABE等待队列:----------------【第5步】持锁线程:A就绪队列:BE等待队列:----------------【第6步】持锁线程:B就绪队列:E等待队列:----------------【第7步】持锁线程:E就绪队列:等待队列:

测试代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;namespace ConsoleApplication1
{public class Class1{public static object locker = new object();public static Stopwatch stopwatch = new Stopwatch();public static int index = 1;public static int start = 0;public static void Main(string[] args){stopwatch.Start();var tasks = new Task[]{Task.Run(delegate { A("A");}),Task.Run(delegate { B("B");}),Task.Run(delegate { C("C");}),Task.Run(delegate { D("D");}),Task.Run(delegate { E("E");}),};while (Console.ReadKey().Key == ConsoleKey.Enter){start++;Console.WriteLine();Console.WriteLine($"【第{start}步】");Thread.Sleep(500); //为避免速度过快而导致步骤错乱}}public static void ConsoleWriteLine(string text) => Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} {stopwatch.ElapsedMilliseconds.ToString().PadLeft(5, ' ')}\t{index++.ToString().PadLeft(2, ' ')}\t{text}");public static void A(string tag){SpinWait.SpinUntil(() => start >= 1);Thread.Sleep(00);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 2);Thread.Sleep(00);ConsoleWriteLine($"{tag}\t[进入等待队列]");if (Monitor.Wait(locker, 3000)){ConsoleWriteLine($"{tag}\t[重新获得锁]");SpinWait.SpinUntil(() => start >= 6);}else{ConsoleWriteLine($"{tag}\t[重新获得锁(超时)]");SpinWait.SpinUntil(() => start >= 8);}ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}public static void B(string tag){SpinWait.SpinUntil(() => start >= 1);Thread.Sleep(10);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 2);Thread.Sleep(20);ConsoleWriteLine($"{tag}\t[进入等待队列]");if (Monitor.Wait(locker))ConsoleWriteLine($"{tag}\t[重新获得锁]");elseConsoleWriteLine($"{tag}\t[重新获得锁(超时)]");SpinWait.SpinUntil(() => start >= 7);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}public static void C(string tag){SpinWait.SpinUntil(() => start >= 1);Thread.Sleep(20);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 3);ConsoleWriteLine($"{tag}\t[PulseAll]");Monitor.PulseAll(locker);SpinWait.SpinUntil(() => start >= 4);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}public static void D(string tag){SpinWait.SpinUntil(() => start >= 2);Thread.Sleep(40);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 5);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}public static void E(string tag){SpinWait.SpinUntil(() => start >= 2);Thread.Sleep(60);ConsoleWriteLine($"{tag}\t[进入就绪队列]");Monitor.Enter(locker);ConsoleWriteLine($"{tag}\t[获得锁]");SpinWait.SpinUntil(() => start >= 8);ConsoleWriteLine($"{tag}\t[退出]");Monitor.Exit(locker);}}
}

测试结果: 

  相关解决方案