Task.ContinueWith和DispatcherSynchronizationContext

时间:2013-12-05 23:19:35

标签: wpf unit-testing task-parallel-library dispatcher

我正在面对一个单元测试使用任务延续和DispatcherSynchrinizationContext的代码时我不明白的问题。

我的单元测试代码:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());

        var class1 = new Class1();
        var result = class1.MyAsyncMethod().Result;

        Assert.IsTrue(result == "OK");
    }
}

正在测试的代码:

class Class1
{
    public Task<string> MyAsyncMethod()
    {
        var tcs = new TaskCompletionSource<string>();

        MyInnerAsyncMethod()
            .ContinueWith(t =>
            {
                // Never reached if TaskScheduler.FromCurrentSynchronizationContext() is set
                tcs.SetResult("OK");

            }, TaskScheduler.FromCurrentSynchronizationContext());

        return tcs.Task;
    }


    private Task<string> MyInnerAsyncMethod()
    {
        var tcs = new TaskCompletionSource<string>();
        tcs.SetResult("OK");
        return tcs.Task;
    }
}

问题是“ContinueWith”方法中包含的代码永远不会到达 IF 我指定“TaskScheduler.FromCurrentSynchronizationContext()”。如果我删除此参数,则继续正确执行...

任何想法或建议?

2 个答案:

答案 0 :(得分:2)

我认为这是因为虽然你已经创建了一个新的DispatcherSynchronisationContext,但是没有实际的线程运行一个调度循环来执行你的任务。

尝试将其放在测试开始时:

// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    // Create our context, and install it:
    SynchronizationContext.SetSynchronizationContext(
        new DispatcherSynchronizationContext(
            Dispatcher.CurrentDispatcher));

    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));

礼貌: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/

答案 1 :(得分:2)

lain 你让我走的正确,谢谢!!

我没有修改我正在测试的代码,这是测试代码的新实现,按预期工作:

[TestClass]
public class UnitTest1
{
    private ExecutionContext _executionContext;

    [TestInitialize]
    public void OnSetup()
    {
        _executionContext = CreateExecutionContext();

        SynchronizationContext.SetSynchronizationContext(_executionContext.DispatcherSynchronizationContext);
    }

    [TestCleanup]
    public void OnTearDown()
    {
        // stops the dispatcher loop
        _executionContext.Dispatcher.InvokeShutdown();
    }

    [TestMethod]
    public void TestMethod1()
    {
        var class1 = new Class1();
        var result = class1.MyAsyncMethod().Result;

        Assert.IsTrue(result == "OK");
    }

    /* Helper classes and methods */

    private ExecutionContext CreateExecutionContext()
    {
        var tcs = new TaskCompletionSource<ExecutionContext>();

        var mockUIThread = new Thread(() =>
                {
                    // Create the context, and install it:
                    var dispatcher = Dispatcher.CurrentDispatcher;
                    var syncContext = new DispatcherSynchronizationContext(dispatcher);

                    SynchronizationContext.SetSynchronizationContext(syncContext);

                    tcs.SetResult(new ExecutionContext
                        {
                            DispatcherSynchronizationContext = syncContext, 
                            Dispatcher = dispatcher
                        });

                    // Start the Dispatcher Processing
                    Dispatcher.Run();
                });

        mockUIThread.SetApartmentState(ApartmentState.STA);
        mockUIThread.Start();

        return tcs.Task.Result;
    }

    internal class ExecutionContext
    {
        public DispatcherSynchronizationContext DispatcherSynchronizationContext { get; set; }
        public Dispatcher Dispatcher { get; set; }
    }

    /*  ------   */

}