检测到没有锁定的方法

时间:2016-10-21 06:37:19

标签: c# .net multithreading thread-safety clr

有没有办法检测我的代码中的某个方法是否在调用堆栈中的任何方法中没有使用任何锁定而被调用?
目标是调试错误的应用程序,并找出某些代码片段是否不是线程安全的。

2 个答案:

答案 0 :(得分:1)

这似乎是AOP(面向方面​​编程)的一个不错的用例。 AOP的一个非常基本的概述是它是一种处理交叉问题的方法,以使代码干燥和模块化。这个想法是,如果你正在对一个对象的每个方法调用做一些事情(例如,记录每个调用)而不是在每个方法的开头和结尾添加一个日志,那么你继承该对象并在类之外执行至于没有浑浊的目的。

这可以通过几种方式完成,我将举两个例子。首先是手动(这不是很好,但可以非常容易地完成小型游戏)。

假设您有一个类,Doer有两个方法Do和Other。你可以继承并制作

public class Doer
{
    public virtual void Do()
    {
        //do stuff.
    }

    public virtual void Other()
    {
        //do stuff.
    }
}

public class AspectDoer : Doer
{
    public override void Do()
    {
        LogCall("Do");
        base.Do();
    }

    public override void Other()
    {
        LogCall("Other");
        base.Other();
    }

    private void LogCall(string method)
    {
       //Record call 
    }
}

如果你只关心一个课程,但如果你必须为很多课程做这件事,很快就会变得不可行。对于这些情况,我建议使用类似CastleProxy库的东西。这是一个动态创建代理以包装所需类的库。与IOC结合使用,您可以轻松地将每项服务包装在您的应用程序中。

以下是使用CastleProxy的一个简单示例,主要观点是使用ProxyGenerator.GenerateProxy并传入IInterceptors来执行方法调用:

    [Test]
    public void TestProxy()
    {
        var generator = new ProxyGenerator();
        var proxy = generator.CreateClassProxy<Doer>(new LogInterceptor());
        proxy.Do();
        Assert.True(_wasCalled);
    }

    private static bool _wasCalled = false;
    public class LogInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Log(invocation.Method.Name);
            invocation.Proceed();
        }

        private void Log(string name)
        {
            _wasCalled = true;
        }
    }

现在,记录部分。我不确定你真的需要这个无锁,短锁可能就够了,但让我们继续想你做。

我不知道C#中有很多支持无锁操作的工具,但我能看到的最简单的版本是使用Interlocked在任何给定时间递增计数器中方法的数量。如果看起来会像这样的东西:

        [Test]
    public void TestProxy()
    {
        var generator = new ProxyGenerator();
        var proxy = generator.CreateClassProxy<Doer>(new LogInterceptor());
        proxy.Do();
        Assert.AreEqual(1, _totalDoCount);
    }

    private static int _currentDoCount = 0;
    private static int _totalDoCount = 0;
    public class LogInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            if (invocation.Method.Name == "Do")
            {
                var result = Interlocked.Increment(ref _currentDoCount);
                Interlocked.Increment(ref _totalDoCount);
                if(result > 1) throw new Exception("thread safe violation");
            }


            invocation.Proceed();
            Interlocked.Decrement(ref _currentDoCount);
        }
    }

Interlocked使用神奇的寄存器魔法进行线程安全操作(我相信比较和交换,但我真的不知道)。如果你需要更多的背景,而不仅仅是“它发生了”。您可以使用无锁的并发堆栈或并发队列(它们也使用互锁:https://msdn.microsoft.com/en-us/library/dd997305.aspx/)。我会在这些上添加一个时间戳,因为我没有充分使用它们来知道它们是否承诺按照它们发生的顺序返回元素。

就像我上面所说,你可能不需要锁定免费操作,但这应该是。我不知道这是否适合你,因为我不知道你的确切问题,但它应该为你提供一些工具来解决这个问题。

答案 1 :(得分:0)

您可以host the CLR yourself,并使用IHostSyncManager::CreateMonitorEvent方法跟踪锁定。然后,您需要将自己的机制从主机公开到名为“IsLockTaken()”的方法。然后,您可以在实际代码中从您的方法中调用它。

我认为这是可能的,但这将是相当多的工作,几乎肯定会完全分散你想要解决的问题,但毫无疑问会很有趣!

这是关于死锁检测https://blogs.msdn.microsoft.com/sqlclr/2006/07/25/deadlock-detection-in-sql-clr/

的有趣读物