检测到没有锁定的方法

use*_*528 13 .net c# clr multithreading thread-safety

有没有办法检测我的代码中的某个方法被调用而不使用调用堆栈中下面的任何方法中的任何锁定?
目标是调试错误的应用程序并查明某些代码段是否不是线程安全的.

JCa*_*der 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 
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您只关心一门课程,那么这很好,但如果您必须为许多课程执行此操作,很快就会变得不可行。对于这些情况,我建议使用 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

我不知道 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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