Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#/.NET 4.5 - Why does "await Task.WhenAny" never return when provided with a Task.Delay in a WPF application's UI thread?

Tags:

c#

.net

delay

task

Given the following code, why does ask.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();
    ...
}
like image 349
Matt Avatar asked Jun 01 '14 21:06

Matt


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


1 Answers

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.

like image 74
Stephen Cleary Avatar answered Sep 19 '22 13:09

Stephen Cleary