【问题标题】:Task.WaitAll freezes app C#Task.WaitAll 冻结应用程序 C#
【发布时间】:2014-01-16 16:45:56
【问题描述】:

我想尝试使用 Threading.Task (C#) 来并行运行一些工作。在这个简单的示例中,我有一个带有进度条和按钮的表单。单击时调用 RunParallel 函数。没有 Task.WaitAll() 它似乎运行良好。但是,使用 WaitAll 语句,表单显示并且没有任何反应。我不明白我在下面的设置中做错了什么。 提前致谢。

public partial class MainWindow : Form
{
    public delegate void BarDelegate();
    public MainWindow()
    {
    InitializeComponent();
    }
    private void button_Click(object sender, EventArgs e)
    {
        RunParallel();
    }
    private void RunParallel() {
        int numOfTasks = 8;
        progressBar1.Maximum = numOfTasks;
        progressBar1.Minimum = 0;
        try
        { 
            List<Task> allTasks = new List<Task>();
            for (int i = 0; i < numOfTasks; i++)
            {
                allTasks.Add(Task.Factory.StartNew(() => { doWork(i); }));
            }
            Task.WaitAll(allTasks.ToArray());
        }
        catch { }  
    }
    private void doWork(object o1)
    {
        // do work...
        // then
        this.Invoke(new BarDelegate( UpdateBar )); 
    }
    private void UpdateBar()
    {
        if (progressBar1.Value < progressBar1.Maximum) progressBar1.Value++;
    }
}

【问题讨论】:

  • Task.WaitAll 是一个阻塞操作。改用 ContinueWith 或 ContinueWhenAll 方法
  • 这就是 WaitAll 所做的,它会阻塞直到所有任务完成。任务无法完成,因为 Invoke 将在 UI 线程上运行其操作,该线程已被 WaitAll 阻止

标签: c# task-parallel-library task


【解决方案1】:

您正在等待 UI 线程。这会冻结 UI。不要那样做。

此外,您正在死锁,因为Invoke 等待 UI 线程解除阻塞。

我的建议:尽可能使用async/await

并且不要吞下异常。你正在以这种方式为自己创造未来的作品。

【讨论】:

  • 感谢您的回答。你能提供一个异步/等待的例子吗?
  • 我只能在这个地方提供非常简短的建议。我宁愿将您定向到google.com/…,以便您获得更全面的信息。
【解决方案2】:

这就是 WaitAll 所做的,它会阻塞直到所有任务完成。任务无法完成,因为 Invoke 将在 UI 线程上运行其操作,该线程已被 WaitAll 阻止

要使您的代码真正异步运行,请尝试以下操作:

private void RunParallel() {
    int numOfTasks = 8;
    progressBar1.Maximum = numOfTasks;
    progressBar1.Minimum = 0;
    try
    { 
        var context=TaskScheduler.FromCurrentSynchronizationContext()
        for (int i = 0; i < numOfTasks; i++)
        {
            Task.Factory.StartNew(()=>DoWork(i))
                    .ContinueWith(()=>UpdateBar(),context);
        }

    }
    catch (Exception exc)
    { 
      MessageBox.Show(exc.ToString(),"AAAAAARGH");
    }  
}
private void DoWork(object o1)
{
    // do work...

}
private void UpdateBar()
{
    if (progressBar1.Value < progressBar1.Maximum) progressBar1.Value++;
}

在这种情况下,每次任务完成时都会在 UI 上下文中调用 UpdateBar,而不会导致任何阻塞。

请注意,这不是生产代码,只是展示如何异步运行方法并更新 UI 而不会阻塞的一种方式。您确实需要阅读 Tasks 以了解它们的作用和工作方式。

在 .NET 4.5+ 中使用 async/await,您可以用更简单的方式编写它。以下将在后台执行 DoWork 而不会阻塞,但每次任务完成时仍会更新 UI。

private async void button1_Click(object sender, EventArgs e)
{
        int numOfTasks = 8;
        progressBar1.Maximum = numOfTasks;
        progressBar1.Minimum = 0;
        try
        {
            for (int i = 0; i < numOfTasks; i++)
            {
                await Task.Run(() => DoWork(i));
                UpdateBar();
            }

        }
        catch (Exception exc)
        {
            MessageBox.Show(exc.ToString(), "AAAAAARGH");
        }
    }

await 告诉编译器生成代码以在其右侧的任务完成时在原始 (UI) 线程上执行其下方的任何内容:

【讨论】:

  • 一个很好的答案,但我真的不能赞成告诉初学者catch (Exception) 然后继续,好像没有错一样。
  • 没有人告诉初学者“继续,好像没有任何问题一样”。事实上,我确实说这不是生产代码。至于你会做什么,它确实取决于我认为的场景:在桌面应用程序上,你告诉用户、记录并可能尝试恢复,或者让应用程序死掉。在服务器应用程序上,您可能不返回任何结果,但会记录问题并向管理员发送警报。在一个演示应用程序上,允许应用程序崩溃或向程序员展示它足以让他修复代码并重试
  • 感谢帕纳吉奥蒂斯! C#4.5 示例效果很好。但是,在 for 循环之后,我想等到每个任务完成。那我该如何重组呢?
  • 您可以使用Task.WhenAll异步等待您传递的所有任务完成,然后在UI线程上执行代码,例如:await Task.WhenAll(myTasks);
【解决方案3】:

在 doWork 中,您调用 this.Invoke(...) 等待 UI 线程处理消息。不幸的是,您的 UI 线程没有处理消息,因为它正在等待所有 doWork(...) 完成。

最简单的解决方法是在 doWork 中将 this.Invoke 更改为 this.BeginInvoke(它会发送消息但不等待消息被处理)。 虽然,我必须管理它仍然不是书本,因为 UI 不应该等待任何事情。 简单模式(预 async/await 时代):

Task.Factory.StartNew(() => {
    ... work ...
})
.ContinueWith((t) => {
    ... updating UI (if needed) ...
}, TaskScheduler.FromCurrentSynchronizationContext());

【讨论】:

    【解决方案4】:

    RunParallel 阻塞,直到所有任务完成。使用不同的机制来通知 UI。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多