简单注入器 - 从后台线程

时间:2017-09-16 21:33:36

标签: c# inversion-of-control simple-injector

我在我正在处理的应用程序中使用SimpleInjector,其中有类似于以下内容:

public class Foo : IFoo
{
    private readonly Bar _bar;

    public Foo(Bar bar)            
    {
        _bar = bar;
    }

    public void DoSomething()
    {                        
        IEnumberable<Order> orders = _bar.Orders;            
    }
}

我的行为是从后台线程(Task.Run)调用Foo.DoSomething并且Bar在应用程序的主方法(Windows窗体应用程序)中以单身生活方式注册。我担心的是,提供给Bar的{​​{1}}的实现是不是线程安全的。

我遇到的主要问题是Foo具有Bar所需的状态,并且在调用Foo之前由主线程设置此状态。我已经四处寻找解决这个问题的方法,但是我无法找到一个有帮助的方法(除非我对此有错误)。

我查看了this页面上的建议,该建议在后台线程上执行实例时使用装饰器。但是这没有用,因为Foo.DoSomething的状态设置在不同的线程(主线程)上,使用装饰器只会创建一个没有状态的Bar的新实例。

所以我想我的问题是我只需要将Bar注册为单例,并确保注册的实现是线程安全的,或者是否有一个明显的解决方案来解决这个问题,我正面对面但是我似乎看不到?

希望我提供的信息足够。如果您需要任何进一步的信息,请告诉我。

由于

更新 Bar只是一个包含应用程序所需信息列表的类。例如:

Bar

以下是我使用Foo的表单应用程序:

public class Bar: IBar
{
    // Not using orders or products, but just for purpose of the example
    // These are initialized early on in the App process because early
    // steps of the app (which are on the main thread) need them.
    public IEnumerable<Order> Orders { get; private set; }

    public IEnumerable<Product> Products { get; private set; }
}

最后这是我的主要方法:

public partial class App: Form
{        
    private readonly IFoo _foo;
    public App(IFoo foo)
    {
        InitializeComponent();
        _foo = foo;            
    }

    public btn1_Click()
    {
        // This is just for the purpose of showing that the data inside Bar 
        // is loaded on the main thread before Foo.DoSomething is run. In 
        // the real app the Bar data is loaded at previous steps of the app 
        // (the app is a wizard like app).
        LoadBarData();
        Task.Run(() =>
        {
            _foo.DoSomething();
        });

        // The issue would be if for example Bar is changed here while the 
        // background thread is running. In my app it doesn't really change 
        // here, but I want to make sure no issues arise in all scenarios, 
        // whether it changes or not.            
    }
}

1 个答案:

答案 0 :(得分:0)

  

Bar在应用程序的主方法(Windows窗体应用程序)中注册,具有单身生活方式。我担心的是,提供给Foo的Bar的实现是不是线程安全的。

如果您的应用程序并行运行访问Bar的操作,您最好确保Bar - 它返回的值 - 是线程安全的。

如果Bar及其值不可变并且在应用程序启动期间只设置一次,那么这不应该是一个问题。如果Bar或其值是可变的,那么事情可能会开始变得更加困难,但这完全取决于您的应用程序的需求。

您应遵循few general rules以防止常见的陷阱,例如:

  • 单身人士应该是线程安全的。
  • 不要从应用程序代码启动后台线程:将其移动到Composition Root。

我们谈到的第一点。第二点是你的情况也出了问题,因为你在点击按钮时调用了Task.Run。通常,应用程序代码不应关心操作是否并行运行。他们应该只使用给定的抽象(在这种情况下为IFoo)。这意味着App只应调用_foo.DoSomething();,而不将其包含在Task.Run中。

如果在后台线程上运行操作很重要,可以创建IFoo的代理或装饰器,将Foo中的真实Task.Run包裹起来。当此代理也实现IFoo时,它可以用作Foo的替代,而App不必知道这一点。

可以使用AsyncMailSenderProxy找到here此模型的示例。