这里的情景:
计时器每分钟调用一个方法。可以通过UI(按钮)调用此方法。我希望如果我的方法是“进行中”,并且被调用,它不会执行该方法两次。
在我的方法中,我使用一个简单的布尔值:
private bool _isProcessing;
public void JustDoIt(Action a, int interval, int times)
{
if (!_isProcessing)
{
_isProcessing = true;
for (int i = 0; i < times; i++)
{
a();
Thread.Sleep(interval);
}
}
_isProcessing = false;
}
工作正常。我用这个测试来测试这个功能:
[Test]
public void Should_Output_A_String_Only_3_Times()
{
var consoleMock = new Mock<IConsole>();
IConsole console = consoleMock.Object;
var doer = new Doer { Console = console };
Action a = new Action(() => console.Writeline("TASK DONE !"));
// Simulate a call by Timer
var taskA = Task.Factory.StartNew(() => doer.JustDoIt(a, 1000, 3));
// Simulate a call by UI
var taskB = Task.Factory.StartNew(() => doer.JustDoIt(a));
taskA.Wait();
consoleMock.Verify(c => c.Writeline("TASK DONE !"), Times.Exactly(3));
}
开发人员检查我的代码并说:“我用lock
关键字替换了你的布尔值。它更多是线程安全。坦率地说,我没有掌握多线程,所以我回答他”好的盖伊!“
几天后(今天更精确),我想测试一下如果使用lock或一个简单的布尔值之间的区别。所以当我用lock关键字替换布尔值时,我很惊讶于这样:
private object _locker = new Object();
public void JustDoIt(Action a, int interval, int times)
{
lock (_locker)
{
//_isProcessing = true;
for (int i = 0; i < times; i++)
{
a();
Thread.Sleep(interval);
}
}
//_isProcessing = false;
}
先例测试未通过:
消息:Moq.MockException:对模拟的预期调用恰好是3次,但是是4次:c =&gt; c.Writeline(“TASK DONE!”)
那么,我是否严重使用lock关键字?应该是'静态'吗?
谢谢
答案 0 :(得分:2)
让_isProcessing
变得不稳定。然后这样做:
public void JustDoIt(Action a, int interval, int times)
{
if (_isProcessing) return
_isProcessing = true;
for (int i = 0; i < times; i++)
{
a();
Thread.Sleep(interval);
}
_isProcessing = false;
}
这有一个小的竞争条件,但由于你的代码无论如何都没有与任何东西同步,我不相信它可能很重要。
答案 1 :(得分:0)
你只需要锁定它,这意味着其他想要进入关键部分的线程等待锁定,如果当前线程/任务释放它们,它们将进入锁定状态。
例如:TaskA获取锁,它现在处于Critical Section并执行方法a()3次。当TaskA完成执行时,它会释放锁定并且可能存在上下文切换,因此TaskB运行方法a()(第4次)。 在TaskB返回之后,主线程说...“嘿,TaskA已经完成,所以我验证了我的结果”
添加到那个,我不知道TaskA是否必须在TaskB之前运行。所以,我不知道Task-Scheduler是否是FIFO。