gpt4 book ai didi

c# - 为什么 Task.ContinueWith 在此单元测试中执行失败?

转载 作者:可可西里 更新时间:2023-11-01 09:06:16 27 4
gpt4 key购买 nike

我遇到了一个单元测试失败的问题,因为 TPL 任务从未执行过它的 ContinueWith(x, TaskScheduler.FromCurrentSynchronizationContext())

问题原来是因为在任务启动之前不小心创建了一个 Winforms UI 控件。

这是一个重现它的例子。您会看到,如果您按原样运行测试,它就会通过。如果您在未注释 Form 行的情况下运行测试,它将失败。

[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Create new sync context for unit test
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

var waitHandle = new ManualResetEvent(false);

var doer = new DoSomethinger();

//Uncommenting this line causes the ContinueWith part of the Task
//below never to execute.
//var f = new Form();

doer.DoSomethingAsync(() => waitHandle.Set());

Assert.IsTrue(waitHandle.WaitOne(10000), "Wait timeout exceeded.");
}
}


public class DoSomethinger
{
public void DoSomethingAsync(Action onCompleted)
{
var task = Task.Factory.StartNew(() => Thread.Sleep(1000));

task.ContinueWith(t =>
{
if (onCompleted != null)
onCompleted();

}, TaskScheduler.FromCurrentSynchronizationContext());
}
}

谁能解释为什么会这样?

我认为这可能是因为使用了错误 SynchronizationContext,但实际上,ContinueWith never完全执行!此外,在这个单元测试中,是否正确的SynchronizationContext是无关紧要的,因为只要在任何线程上调用了waitHandle.set(),测试应该通过。

最佳答案

我忽略了您代码中的注释部分,确实在取消注释 var f = new Form();

时失败了

原因很微妙,如果 Control 类发现 SynchronizationContext.Currentnull,它会自动将同步上下文覆盖为 WindowsFormsSynchronizationContext 或其类型为 System.Threading.SynchronizationContext

一旦 Control 类用 WindowsFormsSynchronizationContext 覆盖 SynchronizationContext.Current,所有对 SendPost 的调用> 期望 Windows 消息循环运行以便工作。在您创建 Handle 并运行消息循环之前,这不会发生。

问题代码的相关部分:

internal Control(bool autoInstallSyncContext)
{
...
if (autoInstallSyncContext)
{
//This overwrites your SynchronizationContext
WindowsFormsSynchronizationContext.InstallIfNeeded();
}
}

可以引用WindowsFormsSynchronizationContext.InstallIfNeeded的源码here .

如果您想覆盖 SynchronizationContext,您需要自定义实现 SynchronizationContext 才能使其正常工作。

解决方法:

internal class MyContext : SynchronizationContext
{

}

[TestMethod]
public void TestMethod1()
{
// Create new sync context for unit test
SynchronizationContext.SetSynchronizationContext(new MyContext());

var waitHandle = new ManualResetEvent(false);

var doer = new DoSomethinger();
var f = new Form();

doer.DoSomethingAsync(() => waitHandle.Set());

Assert.IsTrue(waitHandle.WaitOne(10000), "Wait timeout exceeded.");
}

以上代码按预期工作:)

或者,您可以将 WindowsFormsSynchronizationContext.AutoInstall 设置为 false,这将防止自动覆盖上面提到的同步上下文。(感谢 OP @OffHeGoes 在评论中提到这一点)

关于c# - 为什么 Task.ContinueWith 在此单元测试中执行失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27191518/

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