2

IDisposables順番に処分する必要があるものが 2 つあります。IDisposable最初の は、2 番目の によって強制終了されるサービスに依存している Rx サブスクリプションを強制終了するため、順序付けは重要IDisposableです。これは、サブスクリプションのサブスクリプションが別のスレッドで発生する必要がある Windows フォーム アプリケーション内にありIObservableますが、監視と破棄は UI スレッドで発生する必要があります。(実際には、順序が保証されている限り、破棄が UI スレッドで発生するかどうかは気にしません。)したがって、コードでは、おおよそ次のようになります (一度縮小すると)。

SomeService = new DisposableService();
Subscription = Foo(someService).SubscribeOn(NewThreadScheduler.Default).ObserveOn(theForm).Subscribe(...)

多くの UI イベントでは、これらの両方を順番に破棄する必要があります (サブスクリプション、次に SomeService)。これを行うためにCompositeDisposable、に加えて Rx を使用ContextDisposableして、同じスレッドでシリアル処理を提供しようとしました。

_Disposable = new CompositeDisposable(new[] {                     
    new ContextDisposable(WindowsFormsSynchronizationContext.Current, Subscription),                     
    new ContextDisposable(WindowsFormsSynchronizationContext.Current, SomeService)});

ただし、上記は機能しません。私のロギングに基づいて_DisposableContextDisposableforSomeServiceは同じスレッドで呼び出されますがContextDisposable、サービスが破棄されると同時に別のスレッドでまだ発生しています (したがって、競合状態と NPE が発生します)。

私は C# をプログラミングしてまだ数週間しか経っていないので、問題はコンテキストとディスパッチャーがどのように機能するかについての私の誤解にあると確信しています。この問題に対する正しいアプローチは何ですか?

4

2 に答える 2

0

私が何かを誤解していない限り、どのスレッドが何を処理するかを制御できます。誰がどのスレッドにサブスクライブするかは問題ではありません。この例を見てください

internal class Program
{
    private static void Main(string[] args)
    {
        ReactiveTest rx1 = null;
        ReactiveTest rx2 = null;

        var thread1 = new Thread(() => rx1 = new ReactiveTest());
        var thread2 = new Thread(() => rx2 = new ReactiveTest());

        thread1.Start();
        thread2.Start();

        Thread.Sleep(TimeSpan.FromSeconds(1));

        thread1.Join();
        thread2.Join();

        rx1.Dispose();
        rx2.Dispose();
    }
}

public class ReactiveTest : IDisposable
{
    private IDisposable _timerObservable;

    private object _lock = new object();

    public ReactiveTest()
    {
        _timerObservable = Observable.Interval(TimeSpan.FromMilliseconds(250)).Subscribe(i => 
            Console.WriteLine("[{0}] - {1}", Thread.CurrentThread.ManagedThreadId, i));
    }

    public void Dispose()
    {
        lock (_lock)
        {
            _timerObservable.Dispose();
            Console.WriteLine("[{0}] - DISPOSING", Thread.CurrentThread.ManagedThreadId);
        }
    }
}

これは出力します

[14] - 0
[7] - 0
[15] - 1
[7] - 1
[14] - 2
[15] - 2
[10] - DISPOSING
[10] - DISPOSING

2 つの別々のスレッドでサブスクライブし、3 番目のスレッドで破棄したことがわかります。サブスクリプションでスレッドセーフなことが必要な場合に備えて、破棄をロックしました。この例では、本当に不要です。

于 2013-01-02T20:15:03.920 に答える