繁体   English   中英

如何在 C# 中实现和单元测试锁

[英]How to implement and unit test a lock in C#

我有一个方法需要以原子方式执行一些操作,并且不应同时执行对该方法的两次调用。 这是我尝试实现一个简单的锁( SomeClass被注册为单例):

public class SomeClass
{
    private bool isRunning;

    public void DoWork()
    {
        if (this.isRunning)
        {
            return;
        }

        try
        {
            this.isRunning = true;
            // do some work that has to be done atomically and takes some time
        }
        catch (Exception e)
        {
            // handle exception
        }
        finally
        {
            this.isRunning = false;
        }
    }
}

首先这是正确的吗? 其次,我该如何单元测试呢?

你的方法的问题是你的线程可以在任何时候被中断,很多时候你的代码可能会工作,事实上,如果实际工作很重要,你的代码将在很大比例的时间内工作,但是它并不健壮,所以看看你的代码,如果this.isRunning == false .... 并且两个线程尝试调用 DoWork ....

           if (this.isRunning)
                {
                    return;
                }
    // first thread gets to here, and gets context switched, then that means 
    // second thread will get to here also as isRunning is still false,
    //  but now we have two threads in this piece of code which is what we wanted to avoid.

                try
                {
// first and second thread will come here now....
                    this.isRunning = true;
// and both will go on to do work....

该框架提供了标准的 Thread 原语来阻止这种情况的发生。 因此,我已将临界区分离到一个单独的类 (JustOne) 中,以便您可以独立于您的工作代码对其进行测试。

public class JustOne
{
    private Object _lock = new Object();

    public bool Do(Action action)
    {
        bool lockTaken = false;
        try
        {
            Monitor.TryEnter(_lock, ref lockTaken);
            if (lockTaken)
            {
                action();
            }
            else
            {
                return false;
            }
        }
        finally
        {
            if (lockTaken)
            {
                Monitor.Exit(_lock);
            }
        }

        return true;
    }
}

然后你的班级看起来像

public class SomeClass
 {
     private readonly JustOne _work;
     public SomeClass()
     {
         _work = new JustOne();
     }

     public bool DoWork()
     {
         return _work.Do(() =>
         {
             // actual work
         });
     }

 }

现在你可以用类似的东西测试 JustOne

        public void TestLock()
        {
            var first = new AutoResetEvent(false);
            var second = new AutoResetEvent(false);
            var work = new JustOne();
            var t = Task.Run(() => work.Do(() =>
            {
                first.Set();
                second.WaitOne();
            }));
            first.WaitOne();
            Assert.False(work.Do(() => { }));
            second.Set();
            t.Wait();
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM