Rob Sedgwick
Rob Sedgwick

Reputation: 4514

Winforms await async/await Task never returns

I have a Winforms exe and from a menu I launch a slow-running process as a Task. It should take about 30 seconds to get the data and then show a dialog. Usually it no longer returns. I catch exceptions and nothing appears in the log so I know it's run ok. The form just never appears, and no CPU time seems to be taking up. Yet I run it in the debugger and step through the code and it works fine. Occasionally it does seem to work on a faster PC. What is happening?

    private async void inPlayRecordToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (!GetClient()) return;
        {
            await Task.Run(() =>
            {
                LaunchForm();
            });
        }
    }

    private async void LaunchForm()
    {
        try
        {
            {
                var inPlayView = new InPlayView();
                await inPlayView.GetData();
                inPlayView.ShowDialog();
            }
        }
        catch (Exception ex)
        {
            Logger.LogMessage(ex.ToString());
        }
    }

Upvotes: 1

Views: 1153

Answers (2)

Sean
Sean

Reputation: 398

I have used async/await on one of my projects and I can't think of a reason to do a ShowDialog in a task. Not sure if this will work but you may want to change your flow a little bit. This should make it more consistent and possibly easier to debug.

private async void inPlayRecordToolStripMenuItem_Click(object sender, EventArgs e) {
    if (!GetClient()) {
        return;
    }

    var playView = await LaunchForm();

    if (playView != null) {
        playView.ShowDialog();
    }
}

private async Task<InPlayView> LaunchForm() {
    try {
        var inPlayView = new InPlayView();
        await inPlayView.GetData();

        return inPlayView;
    } catch (Exception ex) {
        // do cleanup of view if needed
        Logger.LogMessage(ex.ToString());
        return null;
    }
}

Upvotes: 0

sellotape
sellotape

Reputation: 8325

Do this instead:

private async void inPlayRecordToolStripMenuItem_Click(object sender, EventArgs e)
{
    if (!GetClient()) return;

    await LaunchForm();
}

private async Task LaunchForm()
{
    try
    {
        var inPlayView = new InPlayView();
        await inPlayView.GetData();
        inPlayView.ShowDialog();
    }
    catch (Exception ex)
    {
        Logger.LogMessage(ex.ToString());
    }
}

You don't want Task.Run() for an already async method, and as a general rule, async void is okay for event handlers only, so not the LaunchForm() method.

Also as a comment points out, Task.Run() queues the task to the ThreadPool, so it will end up off the UI thread.

Upvotes: 1

Related Questions