【问题标题】:How can I achieve that application remains responsive, while threads work in the background?我怎样才能使应用程序保持响应,而线程在后台工作?
【发布时间】:2017-01-03 09:44:48
【问题描述】:

我有一个简单的应用程序,它会告诉我一个范围内的素数数量:

    private void btnMultiThread_Click(object sender, RoutedEventArgs e)
    {
        var numbers = Enumerable.Range(2, range - 1);
        var totalPrimes = numbers
            .AsParallel()
            .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
            .Count();
        MessageBox.Show(totalPrimes.ToString());
    }

问题是,当整个事情都在计算时,wpf 应用程序仍然没有响应:例如,我单击的按钮仍然被按下,应用程序窗口无法移动甚至关闭。

我该怎么做才能解决这个问题? 谢谢

【问题讨论】:

  • @RandomStranger 将一事无成。 async 允许您等待。它不会让任何东西异步运行
  • @PanagiotisKanavos 我的错,删除了我的评论

标签: c# wpf linq


【解决方案1】:

这与多线程本身无关。 Parallel 和 PLINQ 使用多个线程包括当前线程并行处理数据。如果您想避免阻塞,则必须在后台任务中执行 PLINQ 查询,例如使用 Task.Run:

private async void btnMultiThread_Click(object sender, RoutedEventArgs e)
{
    var numbers = Enumerable.Range(2, range - 1);
    var totalPrimes = await Task.Run(()=>
        numbers
        .AsParallel()
        .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
        .Count());

    MessageBox.Show(totalPrimes.ToString());
}

或者让它更干净一点:

private async void btnMultiThread_Click(object sender, RoutedEventArgs e)
{
    var numbers = Enumerable.Range(2, range - 1);
    var totalPrimes = await Task.Run(()=>CalculatePrimes(numbers));
    MessageBox.Show(totalPrimes.ToString());
}

private int CalculatePrimes(IEnumerable<int> numbers)
{
    return numbers
          .AsParallel()
          .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
          .Count());
}

【讨论】:

  • 感谢它完美运行! (特别是我在更清洁的版本中删除了 .Count 之后的额外关闭);)
【解决方案2】:

摘自微软关于Chaining Tasks by Using Continuation Tasks:

在异步编程中,一个异步是很常见的 操作完成后,调用第二个操作并传递数据 给它。传统上,这是通过使用回调方法完成的。在 任务并行库,提供相同的功能 继续任务。延续任务(也称为 continuation) 是由另一个任务调用的异步任务, 这被称为先行词,当先行词结束时。

这是一种方法:

private void btnMultiThread_Click(object sender, RoutedEventArgs e)
{
    Task<int>.Factory.StartNew(() =>
    {
        var numbers = Enumerable.Range(2, range - 1);
    var totalPrimes = numbers
            .AsParallel()
            .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
            .Count();
        return totalPrimes;
    }).ContinueWith(() => MessageBox.Show(antecendent.Result.ToString()); // Antecedent result feeds into continuation
}

【讨论】:

  • 您不需要在延续 lambda 中声明先行词以便在 lambda 正文中使用它吗(先行词 => MessageBox.Show(先行词.Result.ToString())?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-01
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
相关资源
最近更新 更多