2014-10-18 97 views
0

我想用Ninject.Extensions.Interception.DynamixProxy创建一个拦截器来记录方法完成时间。多线程环境中的Ninject拦截

在单线程环境是这样工作的:

public class TimingInterceptor : SimpleInterceptor 
{ 
    readonly Stopwatch _stopwatch = new Stopwatch(); 
    private bool _isStarted; 


    protected override void BeforeInvoke(IInvocation invocation) 
    { 
     _stopwatch.Restart(); 
     if (_isStarted) throw new Exception("resetting stopwatch for another invocation => false results"); 
     _isStarted = true; 
     invocation.Proceed(); 
    } 

    protected override void AfterInvoke(IInvocation invocation) 
    { 
     Debug.WriteLine(_stopwatch.Elapsed); 
     _isStarted = false; 
    } 
} 

在多线程的情况下,这将然而,无法工作,因为秒表的调用共享。如何将StopWatch的实例从BeforeInvoke传递到AfterInvoke,以便在调用之间不共享?

回答

3

这应该在多线程应用程序中工作得很好,因为每个线程都应该有自己的对象图。所以当你开始处理一些任务时,你首先解决一个新的图形,图形不应该从一个线程传递到另一个线程。这使得知道什么是线程安全的(以及不是什么)集中到应用程序中的一个地方,即将所有东西连接起来:composition root

当你这样工作时,这意味着当你使用这个拦截器来监视单例类(并在线程中使用)时,每个线程仍然会获得自己的拦截器(当它被注册为瞬态时),因为每次你解决你得到一个新的拦截器(即使你重复使用相同的“拦截”实例)。

但是这确实意味着您必须非常小心将此截取的组件注入到的位置,因为如果将此截取的对象注入另一个单例中,您将再次遇到麻烦。这种特殊的'麻烦'被称为captive dependency又名生活方式不匹配。很容易意外地错误地配置你的容器,让你陷入麻烦,不幸的是Ninject没有可能警告你。

请注意,即使您开始使用装饰器,而不是拦截器,您的问题也会消失,因为使用装饰器,您可以将所有内容都保存在一个方法中。这意味着即使装饰器也可以是单件,而不会导致任何线程问题。例如:

// Timing cross-cutting concern for command handlers 
public class TimingCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> 
{ 
    private readonly ICommandHandler<TCommand> decoratee; 

    public TimingCommandHandlerDecorator(ICommandHandler<TCommand> decoratee) 
    { 
     this.decoratee = decoratee; 
    } 

    public void Handle(TCommand command) 
    { 
     var stopwatch = Stopwatch.StartNew(); 
     this.decoratee.Handle(command); 
     Debug.WriteLine(stopwatch.Elapsed); 
    } 
} 

当然,使用装饰的往往只是可能,当正确应用SOLID原则,你的设计,因为你经常需要有一些明确的通用抽象能够装饰适用于大系统中的类别范围。在旧版代码库中使用装饰器可能令人望而生畏。