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);}}
}
测试结果: