-1

I have a function that gets a Func as a parameter and executes it inside of a task in a async method in order to display indeterminate progress bar while the function is working....

private async void Invoke(Func<string> function)
{
       if(calculation?.Status == TaskStatus.Running) await calculation;
       calculation = Task.Run(function);
       InvokeA(() => prg.Height = double.NaN);
       InvokeA(() => prg.Visibility = Visibility.Visible);
       Result = await calculation;
       InvokeA(() => prg.Visibility = Visibility.Hidden);
       InvokeA(() => prg.Height = 0);
}

The problem is when ever the function reaches the:

Result = await Calculation;

it sort of stops... it never gets to actually setting the value or closing the progress bar.

Invoke Is Called from a Textbox.KeyDown Method if the key is Enter:

if (SpecialCommands.DoesExist(Text))
    {
          Invoke(() => SpecialCommands.Invoke(Parameter));
          if (!string.IsNullOrEmpty(Result)) Text += $"   --->   {Result}";
     }

variables definition:

calculation = Task<string>
Result = string
prg = ProgressBar
Parameter = string that isn't connected to the UI
David Shnayder
  • 333
  • 4
  • 14
  • 3
    Search for "async Task.Run on GUI thread" and you'll find the cause of your deadlock. – CodeCaster Sep 16 '15 at 17:39
  • Where is `calculation` set, and what is its type? Typically you'd use the `await` keyword along with the actual invocation of an `async` method. – James Wright Sep 16 '15 at 17:40
  • Instead of thank you notes (removed) please provide sample code that demonstrates the problem. Code you've shown so far should work. Also make sure to show stacks of interesting thereads when "it just stops" and check out similar posts about "C# async Wait deadlock" while updating the post. – Alexei Levenkov Sep 16 '15 at 17:40
  • 1
    Side note: "crash" and "stops" are generally different things when you talk about program. Synonym for "stops" would be "freezes"; "crash" generally implies exception/termination. – Alexei Levenkov Sep 16 '15 at 17:42
  • @JamesWright calculation is a Task and Result is a string – David Shnayder Sep 16 '15 at 17:42
  • @AlexeiLevenkov point taken, the problem is STOP – David Shnayder Sep 16 '15 at 17:44
  • @CodeCaster the task does calculation on string that is not connected to any UI element, so this is not the problem... – David Shnayder Sep 16 '15 at 17:46
  • And where do you call Invoke from? Show all relevant code. – CodeCaster Sep 16 '15 at 17:49
  • So you _are_ calling Invoke from the UI thread. http://stackoverflow.com/questions/15021304/an-async-await-example-that-causes-a-deadlock – CodeCaster Sep 16 '15 at 18:08
  • @CodeCaster the UI thread is through the dispatcher, and I do not call the invoke method from the dispatcher... I don't see why you think its from the UI thread nor do I understand anything from the link you posted... – David Shnayder Sep 16 '15 at 18:16
  • Except that you are using the `Result` w/o `await`ing the `Invoke` call, everything looks fine and should work. I've tried it and indeed it's working. So either your `SpecialCommand` does not return, or there is another code not shown here that is causing the problem. Can't you just break all in the debugger and see what happens? – Ivan Stoev Sep 16 '15 at 19:05
  • @IvanStoev I have checked the debugger but I didn't go inside SpecialCommand, I will try that now, but what is the problem with the `Result` w/o `Await`? – David Shnayder Sep 16 '15 at 19:12
  • @IvanStoev I tried and I cannot step inside the `Task.Run` and when ever the debugger reaches the `Await` the application crashes and throws a `NullReference` Exception... – David Shnayder Sep 16 '15 at 19:18
  • The problem is that it's not ready yet because `Invoke` is `async` method and returns immediately (when hith the `await`). I meant not to try stepping, but when by your explanation the code seems to be frozen, select debug break all and use threads view to see different call stacks and what's going on, etc. – Ivan Stoev Sep 16 '15 at 19:18
  • @IvanStoev so how can I solve that? – David Shnayder Sep 16 '15 at 19:20

1 Answers1

1

I've created a standard new WPF application project with the following XAML

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Canvas>
        <Button x:Name="button" Content="Button" Canvas.Left="62" Canvas.Top="40" Width="75" Click="button_Click"/>
        <ProgressBar x:Name="prg" Height="23" Canvas.Left="62" Canvas.Top="265" Width="347" IsIndeterminate="True"/>
    </Canvas>
</Window>

and code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    Task<string> calculation;
    string Result;
    string Text;

    private async void button_Click(object sender, RoutedEventArgs e)
    {
        await Invoke(() => Foo());
        if (!string.IsNullOrEmpty(Result)) Text += $"   --->   {Result}";
    }

    private async Task Invoke(Func<string> function)
    {
        if (calculation?.Status == TaskStatus.Running) await calculation;
        calculation = Task.Run(function);
        InvokeA(() => prg.Height = double.NaN);
        InvokeA(() => prg.Visibility = Visibility.Visible);
        Result = await calculation;
        InvokeA(() => prg.Visibility = Visibility.Hidden);
        InvokeA(() => prg.Height = 0);
    }

    private void InvokeA(Action a) { a(); }

    static string Foo()
    {
        Thread.Sleep(5000);
        return "Bar";
    }
}

and it's working as expected. So the problem is somewhere else from what you provided.

EDIT Based on your comment, it's worth trying the following, because that was the incorrect part of your code:

First, change the signature of the Invoke method to be

private async Task Invoke(Func<string> function)

and then use

await Invoke(() => Foo());
if (!string.IsNullOrEmpty(Result)) Text += $"   --->   {Result}";

Hope that helps.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • After debugging and changing some stuff I still have the problem but now I see that the Invoke Method is executed and the line after where I called it does not get executed... `Invoke(() => Foo());` this is executed, `if (!string.IsNullOrEmpty(Result)) Text += $" ---> {Result}";` this is not – David Shnayder Sep 16 '15 at 19:36
  • 1
    Ha, that's because `Result` is null or empty! – Ivan Stoev Sep 16 '15 at 19:41
  • that's not the case, I didn't mean the `Text` didn't change, I mean the debugger just skips that whole line of the `if`, it doesn't even check it... – David Shnayder Sep 16 '15 at 20:14
  • Then I have no more ideas. As you probably have seen, my both test samples are working. Good luck! – Ivan Stoev Sep 16 '15 at 20:20
  • actually I tried changing the method signature as you did in the EDIT, now everything works... – David Shnayder Sep 16 '15 at 20:22