即使没有调用ConfigureAwait,也等待继续工作线程(false)

时间:2016-01-11 23:01:36

标签: c# multithreading asynchronous

我有一个正在等待UI线程的异步函数。我确信ConfigureAwait(false)没有被召唤。事实上,我已经尝试过明确地调用ConfigureAwait(true)来确定。但是,当等待的任务完成时,它继续在工作线程上。经过一番挖掘后,我偶然发现了this问题,这至少指出了我可能的原因。我添加了

var context = SynchronizationContext.Current;
var scheduler = TaskScheduler.Current;

就在等待的函数之前,我可以看到TaskAwaiter是否能够捕获上下文。似乎上下文为null但为调度程序分配了有效的引用。但是,当等待的任务完成时,它仍在继续工作线程。所以我挖了一点。

使用Resharper我在Task.SetContinuationForAwait中找到了这个小宝石:

// If the user wants the continuation to run on the current "context" if there is one...
if (continueOnCapturedContext)
{
    // First try getting the current synchronization context.
    // If the current context is really just the base SynchronizationContext type, 
    // which is intended to be equivalent to not having a current SynchronizationContext at all, 
    // then ignore it.  This helps with performance by avoiding unnecessary posts and queueing
    // of work items, but more so it ensures that if code happens to publish the default context 
    // as current, it won't prevent usage of a current task scheduler if there is one.
    var syncCtx = SynchronizationContext.CurrentNoFlow;
    if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
    {
        tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark);
    }
    else
    {
        // If there was no SynchronizationContext, then try for the current scheduler.
        // We only care about it if it's not the default.
        var scheduler = TaskScheduler.InternalCurrent;
        if (scheduler != null && scheduler != TaskScheduler.Default)
        {
            tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark);
        }
    }
}

我在两个if语句上设置了断点,发现syncCtxscheduler都是null,这就解释了为什么等待的任务在工作线程上继续,但它没有解释为什么SynchronizationContext.Current在UI线程上为null,或者为什么TaskScheduler.Current在将要调用continuation时为null。

1 个答案:

答案 0 :(得分:1)

事实证明问题是尝试使用包含来自本机Win32应用程序的WPF表单的类库的结果。普通的WinForms或WPF应用程序将初始化 @Test public void testHomeScreenRoot() { WebDriver driver = new HtmlUnitDriver(); driver.get("http://127.0.0.1:9999/tdd"); WebElement element = driver.findElement(By.name("username")); element.sendKeys("root"); element = driver.findElement(By.name("password")); element.sendKeys("rootpasswd"); element.submit(); //Waiting here (new WebDriverWait(driver, 10)).until((WebDriver d) -> d.getTitle().equals("Favorites: root")); //Error here element = driver.findElement(By.name("newusername")); String tagName = element.getTagName(); assertEquals("input", tagName); element = driver.findElement(By.name("newpassword")); tagName = element.getTagName(); assertEquals("input", tagName); element = driver.findElement(By.name("Add user")); tagName = element.getTagName(); assertEquals("input", tagName); String type = element.getAttribute("type"); assertEquals("submit", type); } 的构造函数中的SynchronizationContext。这通常发生在"停车"形式。

由于我无法解释的原因,无论创建了多少控件,Control都从未初始化。在创建任何表单之前手动创建它允许SynchronizationContext ed任务在正确的线程上继续。

await