C#/。NET 4.5 - 为什么"等待Task.WhenAny"当与WPF应用程序的UI线程Task.Delay提供有去无回? [英] C#/.NET 4.5 - Why does "await Task.WhenAny" never return when provided with a Task.Delay in a WPF application's UI thread?

查看:191
本文介绍了C#/。NET 4.5 - 为什么"等待Task.WhenAny"当与WPF应用程序的UI线程Task.Delay提供有去无回?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于以下code,为什么Task.WhenAny从来没有当与1秒的Task.Delay提供的回报?从技术上讲,我不知道,如果在一定时间延长量后返回,但它没有15秒左右后,之后我手工杀掉该进程。根据该文件,我应该不需要手动启动delayTask,事实上,我收到一个异常,如果我尝试手动操作。

在code是从当用户选择在WPF应用程序上下文菜单项UI线程调用,虽然它工作正常,如果我有上下文菜单项指定的点击方式运行此code在一个新的线程。

 公共无效ContextMenuItem_Click(对象发件人,RoutedEventArgs E)
{
    ...
    的someMethod();
    ...
}

公共无效的someMethod()
{
    ...
    SomeOtherMethod();
    ....
}

公共无效SomeOtherMethod()
{
    ...
    TcpClient的客户端=连接()结果。
    ...
}

//如果你想知道下面的覆盖,这些方法是
//不同类型的我只是简单的东西在这里一下,让我不发布
//价值code页面。
公共重写异步任务< TcpClient的>连接()
{
    ...
    任务connectTask = tcpClient.ConnectAsync(URI.Host,URI.Port);
    任务delayTask = Task.Delay(1000);
    如果(等待Task.WhenAny(connectTask,delayTask)== connectTask)
    {
        Console.Write(连接\ N);
        ...
        返回的TcpClient;
    }
    Console.Write(超时的\ n);
    ...
    返回null;
}
 

如果我改变ContextMenuItem_Click以下能正常工作

 公共无效ContextMenuItem_Click(对象发件人,RoutedEventArgs E)
{
    ...
    新的Thread(()=>的someMethod())。启动();
    ...
}
 

解决方案

我predict进一步您的调用堆栈,你调用 Task.Wait 任务< T>。结果。这将导致死锁的我完全解释在我的博客。

总之,什么情况是,等待将(默认)捕获当前的背景,并用它来恢复其异步方法。在这个例子中,背景是WPF界面上下文

所以,当你的code做了等待 WhenAll 返回的任务,它抓住了WPF UI方面。后来,当任务完成时,它会尝试恢复在UI线程上。但是,如果UI线程被阻塞(即打电话给等待结果),那么异步方法不能继续运行,绝不会完成它返回的任务。

正确的解决方案是使用等待,而不是等待结果。这意味着您的通话code将需要异步,它会传播通过你的code基地。最后,你需要决定如何让你的UI异步的,这是一门艺术本身。至少要开始,你需要一个异步无效事件处理程序或某种异步MVVM命令(我探索的异步MVVM在MSDN文章命令)。从那里,你需要设计一个合适的异步UI;也就是说,如何你的UI的外观和它允许在异步操作正在进行什么操作。

Given the following code, why does Task.WhenAny never return when provided with a Task.Delay of 1 second? Technically I'm not sure if it does return after a extended amount of time, but it doesn't after 15 seconds or so after which I manually kill the process. According to the documentation I shouldn't be required to manually start the delayTask, and in fact I receive a exception if I try to do so manually.

The code is being called from the UI thread when a user selects a context menu item in a WPF application, although it works fine if I have the click method specified for the context menu item run this code in a new thread.

public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
    ...
    SomeMethod();
    ...
}

public void SomeMethod()
{
    ...
    SomeOtherMethod();
    ....
}

public void SomeOtherMethod()
{
    ...
    TcpClient client = Connect().Result;
    ...
}

//In case you're wondering about the override below, these methods are in
//different classes i've just simplified things here a bit so I'm not posting
//pages worth of code.
public override async Task<TcpClient> Connect()
{
    ...
    Task connectTask = tcpClient.ConnectAsync(URI.Host, URI.Port);
    Task delayTask = Task.Delay(1000);
    if (await Task.WhenAny(connectTask, delayTask) == connectTask)
    {
        Console.Write("Connected\n");
        ...
        return tcpClient;
    }
    Console.Write("Timed out\n");
    ...
    return null;
}

If I change ContextMenuItem_Click to the following it works fine

public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
    ...
    new Thread(() => SomeMethod()).Start();
    ...
}

解决方案

I predict that further up your call stack, you're calling Task.Wait or Task<T>.Result. This will cause a deadlock that I explain in full on my blog.

In short, what happens is that await will (by default) capture the current "context" and use that to resume its async method. In this example, the "context" is the WPF UI context.

So, when your code does its await on the task returned by WhenAll, it captures the WPF UI context. Later, when that task completes, it will attempt to resume on the UI thread. However, if the UI thread is blocked (i.e., in a call to Wait or Result), then the async method cannot continue running and will never complete the task it returned.

The proper solution is to use await instead of Wait or Result. This means your calling code will need to be async, and it will propagate through your code base. Eventually, you'll need to decide how to make your UI asynchronous, which is an art in itself. At least to start with, you'll need an async void event handler or some kind of an asynchronous MVVM command (I explore async MVVM commands in an MSDN article). From there you'll need to design a proper asynchronous UI; i.e., how your UI looks and what actions it permits when asynchronous operations are in progress.

这篇关于C#/。NET 4.5 - 为什么&QUOT;等待Task.WhenAny&QUOT;当与WPF应用程序的UI线程Task.Delay提供有去无回?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆