组成:(以单个object为准)
1、[持锁线程] = locker = 持锁中
2、[就绪队列] = ready queue = 一等队列,队列中的线程将被挂起等待,系统会自动按顺序为线程分配对象的排他锁
3、[等待队列] = wait queue = 二等队列,队列中的线程将被挂起等待,需要通过Pulse来控制向前
模拟效果:
//线程A
1 lock (locker)
{
//线程B //线程C
2 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[重新获得锁]");
else
ConsoleWriteLine($"{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);
}
}
}
测试结果:
模拟效果:
//线程A //线程B //线程C
1 lock (locker) lock (locker) lock (locker)
{ { {
2 Monitor.Wait(1000) Monitor.Wait
//线程D //线程E
lock (locker) lock (locker)
{ {
3 Monitor.PulseAll
4 }
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[重新获得锁]");
else
ConsoleWriteLine($"{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);
}
}
}
测试结果: