3

这里的场景:

计时器每分钟调用一个方法。可以通过 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关键字替换了您的布尔值。它更线程安全。坦率地说,我没有掌握多线程,所以我回答他“OK Guy!”

几天后(今天更准确地说),我想测试使用锁或简单布尔值之间的区别。因此,当我用 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=>c.Writeline("TASK DONE !")

那么,我是否错误地使用了 lock 关键字?它应该是“静态的”吗?

谢谢

4

2 回答 2

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;
}

这有一个轻微的竞争条件,但由于您的代码无论如何都没有同步到任何东西,我认为它可能无关紧要。

于 2012-06-19T11:33:49.410 回答
0

您只需锁定它,这意味着其他想要进入临界区的线程等待锁定,如果当前线程/任务释放它,他们将进入锁定。

例如:TaskA 获得锁,它现在处于临界区并执行方法 a() 3 次。当 TaskA 执行完毕后,它会释放锁,可能会有上下文切换,所以 TaskB 运行方法 a()(第四次)。TaskB 返回后,主线程说..“嘿,TaskA 已经完成,所以我验证我的结果”

除此之外,我不知道TaskA是否必须在TaskB之前运行。所以,我不知道 Task-Scheduler 是否是 FIFO。

于 2012-06-19T11:37:13.507 回答