我想在这里扩展 dbaseman 的答案。具体来说,他的DoWorkIfNotBusy
方法。
我认为这是一个不好的例子(由于可能的竞争条件),并且想展示我认为正确的方式是:
private static readonly object lockobj = new object();
public bool DoWorkIfNotBusy()
{
bool lockWasTaken = false;
var temp = lockobj;
try
{
Monitor.TryEnter(temp, ref lockWasTaken);
if (lockWasTaken) // This crucial test was missing! (Added 2021-04-08)
{
//Do work here..
}
}
finally
{
if (lockWasTaken) Monitor.Exit(temp);
}
return lockWasTaken;
}
2021-04-08 更新:增加了当前线程是否成功获取锁的测试。如果不是,则不应执行关键部分中的代码。
2021-04-09 更新:下面的代码显示需要检查lockWasTaken
,作为单元测试。
[TestClass]
public class DummyTests
{
private static readonly object LockObj = new object();
[TestMethod]
public void TestMonitor()
{
Thread trd1 = StartThread();
Thread.Sleep(100);
Thread trd2 = StartThread();
Thread.Sleep(1000);
Thread trd3 = StartThread();
while (trd1.IsAlive || trd2.IsAlive || trd3.IsAlive)
{
Thread.Sleep(100);
}
}
private Thread StartThread()
{
var thread = new Thread(parameter => this.ThreadTask());
thread.Start(nameof(thread));
Trace.WriteLine($"Started thread {thread.ManagedThreadId}.");
return thread;
}
private void ThreadTask()
{
bool lockAcquired = false;
try
{
Monitor.TryEnter(LockObj, ref lockAcquired);
if (lockAcquired)
{
Trace.WriteLine($"Lock acquired by thread {Thread.CurrentThread.ManagedThreadId}.");
Thread.Sleep(1000);
}
else
{
Trace.WriteLine($"Lock denied to thread {Thread.CurrentThread.ManagedThreadId}.");
}
}
finally
{
if (lockAcquired)
{
Monitor.Exit(LockObj);
Trace.WriteLine($"Lock released by thread {Thread.CurrentThread.ManagedThreadId}.");
}
}
}
}
输出将是这样的:
Started thread 12.
Lock acquired by thread 12.
Started thread 13.
Lock denied to thread 13.
Lock released by thread 12.
Started thread 14.
Lock acquired by thread 14.
Lock released by thread 14.