gpt4 book ai didi

c# - Rx TestScheduler 在后台线程计划操作时抛出空引用异常

转载 作者:太空宇宙 更新时间:2023-11-03 13:15:45 24 4
gpt4 key购买 nike

根据一些评论,我应该明确表示这个问题是关于为什么 TestScheduler 会抛出空引用异常,而不是如何让测试通过。一个较早的示例假定与 TPL 的交互是问题的原因,但我现在发现这不是触发行为所必需的,因此我用更简单的测试用例替换了代码

我在尝试将 rx 测试“虚拟时间”TestScheduler 与后台线程结合时遇到了一些问题。我发现证明问题的最简单方法显示在帖子的底部。

代码只是运行一个后台线程,该线程订阅一个带有超时的可观察序列。

超时是由 TestScheduler 驱动的,当我在主线程上推进它时,会生成空引用异常:

Assert.Fail failed. Virtual time 00:00:00.1394720, exception System.NullReferenceException: Object reference not set to an instance of an object. at System.Reactive.Concurrency.VirtualTimeScheduler2.GetNext()
at System.Reactive.Concurrency.VirtualTimeSchedulerBase
2.AdvanceTo(TAbsolute time) at System.Reactive.Concurrency.VirtualTimeSchedulerBase`2.AdvanceBy(TRelative time) at UnitTestProject1.UnitTest1.d__6.MoveNext() in .......\UnitTest1.cs:line

失败似乎对所使用的 IObservable 的确切类型不敏感,但似乎取决于超时选择器的存在。有趣的是,测试通常在超时因触发之前的虚拟时间失败。

运行与控制台应用程序相同的代码似乎也能正常工作,尽管该问题似乎很容易被扰乱(可能是竞争条件),因此这可能是转移注意力。

进一步的研究和评论强烈暗示该行为是由于当后台线程安排其超时操作时调度程序提前时发生的竞争条件

在此先感谢您提供的任何信息...

using Microsoft.Reactive.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Reactive.Linq;
using System.Threading;

namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestBackgroundThreadWithTestScheduler()
{
var scheduler = new TestScheduler();
var seq = Observable.Never<string>();

bool subscribed = false;
ThreadPool.QueueUserWorkItem(_ =>
{
Thread.Sleep(100); //wait a bit to give main thread chance to start advancing scheduler
seq.Timeout(TimeSpan.FromSeconds(10), scheduler)
.Subscribe(s => {/*never called*/});
subscribed = true; //signal we're subscribed
});

//---- Uncommenting this line to avoid the race condition appears to fix the test ----
//while (!subscribed) Thread.Yield();

//Advance the scheduer in small increments to maximise our chances of hitting the race
var watch = scheduler.StartStopwatch();
try
{
while (watch.Elapsed < TimeSpan.FromSeconds(20)) scheduler.AdvanceBy(10);
}
//NullReference is thrown unexpectedly
catch (NullReferenceException ex)
{
Assert.Fail("Virtual time {0}, exception {1}", watch.Elapsed, ex);
}
catch (TimeoutException)
{
//desired result is a TimeoutException so this is a test pass
}
}
}
}

最佳答案

堆栈跟踪 System.Reactive.Concurrency.VirtualTimeScheduler2.GetNext() 中的这一行是一个线索,表明错误确实是您正在从 2 个线程访问调度程序( TestScheduler 是不是线程安全的 当前在 TestScheduler 中有 a bug 当从多个线程访问它时)。

您的第一条评论可能是正确的:后台任务正在添加到调度程序,就像您的其他任务正在推进调度程序一样。

尝试在访问调度程序的语句周围加一个锁,看看是否能解决您的问题。

当然,真正的解决办法是让后台任务在设置好后发出信号,让测试任务在继续之前等待信号。因为即使 TestScheduler 没有它的错误,您的测试也有一个竞争条件,即测试线程可以在后台线程订阅可观察对象之前完成。

关于c# - Rx TestScheduler 在后台线程计划操作时抛出空引用异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26287710/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com