这似乎是 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 来增加在任何给定时间方法中有多少实例的计数器 If会看起来像这样:
[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/)。不过,我会在它们上加上一个时间戳,因为我没有充分使用它们来知道它们是否承诺按照它们发生的顺序返回元素。
就像我上面所说的,您可能不需要无锁操作,但这应该。我不知道这是否适合您,因为我不知道您的确切问题,但它应该为您提供一些解决此问题的工具。