运行异步任务并调用winform控件

时间:2017-07-07 04:31:34

标签: c# winforms asynchronous task

如果有这段代码:

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");

这是日志功能:

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);
}

当我运行它时,我可以看到代码试图调用listView1。但在那之后,应用程序挂起并且根本不会继续。当我删除Log()并再次运行它时,它可以很好地工作。

如何使用日志记录运行此代码?

1 个答案:

答案 0 :(得分:1)

您正在以危险的方式使用InvokeRequiered / Invoke模式。

问题是您使用InvokeRequired检查您的线程是否可以从ListView1调用函数。如果没有,则不使用Invoke调用ListView1函数,而是调用自己的函数(this.Log)。

实现此模式的正确方法是检查是否需要调用适用于您自己的表单。问:this.InvokeRequired而不是listview1.InvokeRequired:

class MyForm : Form
{
    ...

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

如果您使用调试器来逐步执行此函数,您将看到需要调用,因此您再次调用相同的函数,之后不再需要调用。

顺便说一下,StackOverFlow: MethodInvoker vs Action for Control.BeginInvoke

中提供了使用MethodInvoker代替Action的原因