ObserveOn(Scheduler.CurrentThread)不会导致订阅的Action在原始线程上运行

时间:2012-11-15 23:56:44

标签: c#-4.0 system.reactive

我有一个Action,它接受一个完成时将使用泛型参数调用的回调,即Action<Action<T>>。我想在操作开始时显示一个忙碌的微调器,然后在调用回调时将其取出,所以我创建了一个简单的实用程序来执行此操作。我遇到的问题是用户希望他们的回调在原始调用线程上运行,但并不总是如此。它几乎总是在单元测试(nUnit)中完美地工作,但在应用程序实际运行时不适用于某些调用(WPF,.Net 4)。

以下是我所拥有的相关内容

void WrapAsyncCallbackPattern<T>(Action<T> callback, Action<Action<T>> actionToRun)
{
    var subject = new AsyncSubject<T>();
    try
    {
        actionToRun(
            result =>
            {
                subject.OnNext(result);
                subject.OnCompleted();
            });
    }
    catch (Exception ex)
    {
        subject.OnError(ex);
    }

    subject
        .ObserveOn(Scheduler.CurrentThread)
        .Subscribe(callback, OnError);
}

我希望callback在我正在订阅的线程上运行(并且subject被声明为on)但它似乎不能可靠地执行。我认为我做的事情很傻。它是什么?

修改:添加了单元测试代码

private readonly TimeSpan m_WaitTime = TimeSpan.FromSeconds(1);

[Test]
public void WrapAsyncCallbackPattern_WithActionOnDifferentThread_CallsCallbackOnSameThread()
{
    var awaiter = new AutoResetEvent(false);

    bool callbackRan = false;
    int callingThreadId = Thread.CurrentThread.ManagedThreadId;
    int callbackThreadId = int.MinValue;
    int actionThreadId = int.MinValue;

    BackgroundOperation.WrapAsyncCallbackPattern<int>(
        _ =>
        {
            callbackRan = true;
            callbackThreadId = Thread.CurrentThread.ManagedThreadId;

            awaiter.Set();
        },
        cb => ThreadPool.QueueUserWorkItem(
            _ =>
            {
                actionThreadId = Thread.CurrentThread.ManagedThreadId;
                cb(0);
            }));

    var errorInfo = string.Format("\r\nCalling thread = {0}; Action thread = {1}; Callback thread = {2}", callingThreadId, actionThreadId, callbackThreadId);

    Assert.IsTrue(awaiter.WaitOne(m_WaitTime));
    Assert.IsTrue(callbackRan);
    Assert.AreNotEqual(callingThreadId, actionThreadId, "Action needs to be run on a different thread for this test." + errorInfo);
    Assert.AreNotEqual(actionThreadId, callbackThreadId, "Callback should not be run on action thread." + errorInfo);
    Assert.AreEqual(callingThreadId, callbackThreadId, "Callback should be run on calling thread." + errorInfo);
}

1 个答案:

答案 0 :(得分:7)

您可能对Scheduler.CurrentThread的作用有错误的理解。我想每个人都会犯这个错误。

CurrentThread调度程序与执行的observable相关,而不是在定义(或订阅)时。考虑延迟执行或延迟执行。这应该是有意义的,因为每当你跳转到另一个线程时,你需要一些编组调用的方法。

所以你真正想要的是这样的事情:

var synchContext = new SynchronizationContextScheduler(
    System.Threading.SynchronizationContext.Current) 

subject
    .ObserveOn(synchContext)
    .Subscribe(callback, OnError);

或者也许:

subject
    .ObserveOn(this) /* this is my current form */
    .Subscribe(callback, OnError);

如果你这样做,你应该能够控制回调运行的线程。

您的测试可能有效,因为它们最终同步执行。