确保多个IDisposables顺序处置 [英] Ensuring sequential disposing of multiple IDisposables
问题描述
我有两个 IDisposables
,我需要有处理顺序。顺序是重要的,因为第一个的IDisposable
杀死是依靠将由第二杀死一个服务的接收订阅的IDisposable
。这是在Windows窗体应用程序,其中的IObservable
认购需要在不同的线程,但在观察和处理需求发生在UI线程上发生。 (其实,我不若处置UI线程上发生的,只要为了保证在意。)所以,在代码中,我大致有以下几种(一次降低):
I have two IDisposables
that I need to have disposed in sequential order. The ordering is important since the first IDisposable
kills an Rx subscription that is relying on a service that will be killed by the second IDisposable
. This is within a Windows Forms application where the subscription of the IObservable
needs to happen on a different thread but the observing and disposing needs to happen on the UI thread. (Actually, I don't care if the disposing happens on the UI thread as long as the order is ensured.) So, in code I have roughly the following (once reduced):
SomeService = new DisposableService();
Subscription = Foo(someService).SubscribeOn(NewThreadScheduler.Default).ObserveOn(theForm).Subscribe(...)
在一些我需要在顺序(订阅然后SomeService)处理这两种的UI事件。要做到这一点,我已经尝试使用接收的除 CompositeDisposable
的 ContextDisposable
来提供相同的线程上的串行处置:
On a number of UI events I need to dispose of both of these in order (Subscription and then SomeService). To do this I have tried using Rx's CompositeDisposable
in addition to the ContextDisposable
to provide serial disposing on the same thread:
_Disposable = new CompositeDisposable(new[] {
new ContextDisposable(WindowsFormsSynchronizationContext.Current, Subscription),
new ContextDisposable(WindowsFormsSynchronizationContext.Current, SomeService)});
以上不工作,但是。根据我的记录 _Disposable
和 ContextDisposable
为 SomeService
是所谓同一个线程,但是,从 ContextDisposable
上的服务不同的线程concurent仍然发生被布置(从而导致比赛条件与NPE)。
The above does not work however. Based on my logging _Disposable
and the ContextDisposable
for SomeService
are called on the same thread but the ContextDisposable
is still occurring on a different thread concurent with the service being disposed (and thus resulting in race conditions and NPEs).
我只编写C#进行了几个星期,所以我敢肯定,这个问题是我的背景和调度员是如何工作的误解。什么是正确的方法解决这个问题?
I've only been programming C# for a couple of weeks and so I'm sure the problem is my misunderstanding of how contexts and dispatchers work. What is the correct approach to this problem?
推荐答案
的 SubscribeOn 调度呼吁双方订阅
和的Dispose
。因此,调用的Dispose
您的订阅的变量,不管执行当前是否在UI线程上与否,在申购结果被调度处置通过 NewThreadScheduler.Default
。
SubscribeOn schedules the calls to both Subscribe
and Dispose
. Therefore, calling Dispose
on your Subscription variable, regardless of whether execution is currently on the UI thread or not, results in the subscription being scheduled for disposal by NewThreadScheduler.Default
.
这是几乎从来没有一个好主意,用 SubscribeOn
;然而,你的情况你声称它解决您的问题的50% - 这比我见过的最用途多50% - 所以我必须问你是否真的需要订阅的后台线程执行中首位。这是创建一个全新的线程,然后就可以调用一个方法非常昂贵,相比之下,只调用UI线程上的方法直接,如果所有的方法确实是开始一些异步工作,如发送网络请求或读取文件。也许,如果计算发送网络消息被证明是过于耗时,然后用 SubscribeOn
可能是正确的;虽然,当然,前提是你要处置计划也是如此。
It's almost never a good idea to use SubscribeOn
; however, in your case you're claiming that it solves 50% of your problem - and that's 50% more than most uses I've seen - so I must question whether or not you actually need the subscription to execute on a background thread in the first place. It's awfully expensive to create a brand new thread and then invoke a method on it, compared to just calling a method on the UI thread directly, if all the method does is begin some asynchronous work such as sending a network request or reading a file. Perhaps if computing a network message to be sent is proven to be overly time-consuming, then using SubscribeOn
may be correct; although, of course, only if you want disposal scheduled as well.
如果订阅您观察到的,必须在后台线程执行,但处理必须保持自由线程,然后再考虑用下面的操盘手(未经测试)。
If the subscription to your observable must execute on a background thread, yet disposal must remain free-threaded, then consider using the following operator instead (untested).
public static class ObservableExtensions
{
public static IObservable<TSource> SubscribeOn<TSource>(
this IObservable<TSource> source,
bool doNotScheduleDisposal,
IScheduler scheduler)
{
if (!doNotScheduleDisposal)
{
return source.SubscribeOn(scheduler);
}
return Observable.Create<TSource>(observer =>
{
// Implementation is based on that of the native SubscribeOn operator in Rx
var s = new SingleAssignmentDisposable();
var d = new SerialDisposable();
d.Disposable = s;
s.Disposable = scheduler.Schedule(() =>
{
d.Disposable = source.SubscribeSafe(observer);
});
return d;
});
}
}
这篇关于确保多个IDisposables顺序处置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!