user1140479
user1140479

Reputation:

Running an async task and invoking winform control

If have this code:

Task[] tasks = new Task[3];


int index = 0;
foreach (KeyValuePair<string, int> packageId in PackageIds)
{
    await Task.Run(() => _logger.Log(packageId.Key));

    tasks[index] = Task.Factory.StartNew(() =>
    {
        Log(packageId.Key);
    });
    index++;
}

Task.WaitAll(tasks);
_logger.Log("all done");

This is the Log function:

private void Log(string message)
{
    ListViewItem lv = new ListViewItem
    {
        Text = $"{DateTime.Now:dd-MM-yyyy hh:mm:ss}"
    };
    lv.SubItems.Add(message);

    if (listView1.InvokeRequired)
        Invoke(new Action(() => Log(message)));
    else
        listView1.Items.Add(lv);
}

When I run it I can see the code tries to invoke the listView1. But after that, the application hangs and doesn't continue at all. When I remove the Log() and run it again, it works perfectly.

How can I run this code with the logging?

Upvotes: 2

Views: 4873

Answers (1)

Harald Coppoolse
Harald Coppoolse

Reputation: 30464

You are using the InvokeRequiered/Invoke pattern in a dangerous way.

The problem is that you use InvokeRequired to check if your thread can call functions from ListView1. If not, you don't use Invoke to call the ListView1 functions, but call your own function (this.Log).

The proper way to implement this pattern is to check if invoke is required is for your own form. Ask: this.InvokeRequired instead of listview1.InvokeRequired:

class MyForm : Form
{
    ...

    private void MyEventHandler(object sender, ...)
    {
        if (this.InvokeRequired)
        {
            Invoke(new MethodInvoker( () => this.MyEventHandler(sender, ...));
        }
    }
    else
    {
        ProcessMyEvent(...);
    }

If you use a debugger to step through this function, you'll see that invoke is required, and thus you invoke the same function again after which invoke is no longer required.

By the way, the reason to use MethodInvoker instead of Action is given in StackOverFlow: MethodInvoker vs Action for Control.BeginInvoke

Upvotes: 4

Related Questions