简体   繁体   English

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

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

I have a method that needs to perform some operations atomically, and also no two calls to this method should be executed simultaneously.我有一个方法需要以原子方式执行一些操作,并且不应同时执行对该方法的两次调用。 Here is my attempt to implement a simple lock ( SomeClass is registered as a singleton):这是我尝试实现一个简单的锁( 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;
        }
    }
}

First of all is this correct?首先这是正确的吗? Secondly, how can I unit test this?其次,我该如何单元测试呢?

The problem with your approach is that your thread can be interrupted at any point, many times your code might work, in fact if the actual work is significant, your code will work a very high percentage of the time, however it's not robust, so having a look at your code, if this.isRunning == false .... and two threads try and call DoWork....你的方法的问题是你的线程可以在任何时候被中断,很多时候你的代码可能会工作,事实上,如果实际工作很重要,你的代码将在很大比例的时间内工作,但是它并不健壮,所以看看你的代码,如果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....

The framework provides standard Thread primitives that will stop that from happening.该框架提供了标准的 Thread 原语来阻止这种情况的发生。 So, I've separated the critical section out into a separate class (JustOne) so you can test it independently of your work code.因此,我已将临界区分离到一个单独的类 (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;
    }
}

Then your class looks like然后你的班级看起来像

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

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

 }

now you can test JustOne with something like现在你可以用类似的东西测试 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