我在我正在处理的应用程序中使用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.
}
}
答案 0 :(得分:0)
Bar在应用程序的主方法(Windows窗体应用程序)中注册,具有单身生活方式。我担心的是,提供给Foo的Bar的实现是不是线程安全的。
如果您的应用程序并行运行访问Bar
的操作,您最好确保Bar
- 它返回的值 - 是线程安全的。
如果Bar
及其值不可变并且在应用程序启动期间只设置一次,那么这不应该是一个问题。如果Bar
或其值是可变的,那么事情可能会开始变得更加困难,但这完全取决于您的应用程序的需求。
您应遵循few general rules以防止常见的陷阱,例如:
我们谈到的第一点。第二点是你的情况也出了问题,因为你在点击按钮时调用了Task.Run
。通常,应用程序代码不应关心操作是否并行运行。他们应该只使用给定的抽象(在这种情况下为IFoo
)。这意味着App
只应调用_foo.DoSomething();
,而不将其包含在Task.Run
中。
如果在后台线程上运行操作很重要,可以创建IFoo
的代理或装饰器,将Foo
中的真实Task.Run
包裹起来。当此代理也实现IFoo
时,它可以用作Foo
的替代,而App
不必知道这一点。
可以使用AsyncMailSenderProxy
找到here此模型的示例。