Reactive Extensions - AsyncLock.Wait抛出ArgumentNullException

时间:2015-07-22 12:58:30

标签: c# system.reactive

使用Observable.Interval时,我得到以下异常导致应用程序崩溃的几次(异常只能在事件查看器中找到 - 错误源:.NET运行时)

Application: RxPlayground.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.ArgumentNullException
Stack:
   at System.Reactive.Concurrency.AsyncLock.Wait(System.Action)
   at System.Reactive.Concurrency.DefaultScheduler+<>c__DisplayClass9`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].<SchedulePeriodic>b__6()
   at System.Reactive.Concurrency.ConcurrencyAbstractionLayerImpl+PeriodicTimer.Tick(System.Object)
   at System.Threading.TimerQueueTimer.CallCallbackInContext(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.TimerQueueTimer.CallCallback()
   at System.Threading.TimerQueueTimer.Fire()
   at System.Threading.TimerQueue.FireNextTimers()
   at System.Threading.TimerQueue.AppDomainTimerCallback()

使用Reactive Extensions时遇到过这样的问题吗?你知道原因是什么吗?

以下是一些可能导致问题的代码(但可能不是因为我无法重现它...):

static void Main(string[] args)
{
    var subscriptions = SubscribeToObservables().Take(10000).ToArray();

    Console.WriteLine("BEGIN. Click Enter to Dispose...");
    Console.ReadLine();

    Console.WriteLine("DISPOSING");

    foreach (var subscription in subscriptions)
    {
        subscription.Dispose();
    }

    Console.WriteLine("DISPOSED. Click Enter to finish...");
    Console.ReadLine();

    Console.WriteLine("END");
}

private static IEnumerable<IDisposable> SubscribeToObservables()
{
    var x = Observable.Interval(TimeSpan.FromSeconds(1)).Select(n =>
    {
        //Thread.Sleep(TimeSpan.FromMinutes(1));
        return n;
    });

    var sub = x.Subscribe(
        n =>
        {
            Thread.Sleep(TimeSpan.FromMinutes(1));
            Console.WriteLine(n);
        });
    yield return sub;
}

编辑:我检查了AsyncLock类及其所有引用(https://github.com/Reactive-Extensions/Rx.NET/blob/master/Rx.NET/Source/System.Reactive.Core/Reactive/Concurrency/AsyncLock.cs),我看不到Action委托可能为空的任何可能性!在我们的异常情况下,我们可以从stacktrace中看到从以下代码(https://github.com/Reactive-Extensions/Rx.NET/blob/master/Rx.NET/Source/System.Reactive.Core/Reactive/Concurrency/DefaultScheduler.cs)调用了Wait:

public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
{
    if (period < TimeSpan.Zero)
        throw new ArgumentOutOfRangeException("period");
    if (action == null)
        throw new ArgumentNullException("action");

    var state1 = state;
    var gate = new AsyncLock();

    var cancel = s_cal.StartPeriodicTimer(() =>
    {
        gate.Wait(() =>
        {
            state1 = action(state1);
        });
    }, period);

    return Disposable.Create(() =>
    {
        cancel.Dispose();
        gate.Dispose();
        action = Stubs<TState>.I;
    });
}

我已经在IL DASM中检查过 System.Reactive.Concurrency.DefaultScheduler +&lt;&gt; c__DisplayClass9`1 [[System .__ Canon,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089] ] .b__6()是为以下lambda编译的:

() =>
{
    gate.Wait(() =>
    {
        state1 = action(state1);
    });
}

看起来lambda () => { state1 = action(state1); }被传递为null。 但是怎么可能?

1 个答案:

答案 0 :(得分:1)

可以重现异常。你必须传递一个选择器函数,它只是null。也许你忘了在每种情况下初始化Select块中的函数。或者当调度程序想要运行它时,类的实例不再存在。这是可能的方式:

Func<long, int> selector = null;
var x =  Observable.Interval(TimeSpan.FromSeconds(1)).Select(selector);