【问题标题】:WPF GUI Performance with a large number of parallel tasks具有大量并行任务的 WPF GUI 性能
【发布时间】:2017-04-29 15:41:59
【问题描述】:

我开发了一个小型客户端 (WPF) 来对我们的系统进行一些压力测试。本质上,它必须在 Asp.Net WebApi 端点上并行调用各种方法。

每次您按下“开始”时,它会生成 4000 个任务(异步 - 等待)与压力请求并行,等待它们全部完成,然后再次执行 - 直到用户单击停止按钮。 GUI 装饰有进度条和一些计数器:错误请求、已完成请求、正在进行的请求。我之所以获得这些信息,是因为发出这批压力请求的对象暴露了一些事件:

var stressTestTask = new stressTestTask(LogService, configuration);

stressTestTask.ErrorRequestCountChanged += stressTestTask_ErrorRequestCountChanged;
stressTestTask.GoodRequestCountChanged += stressTestTask_GoodRequestCountChanged;
stressTestTask.TryRequestCountChanged += stressTestTask_TryRequestCountChanged;

_executionCancellationToken = new CancellationTokenSource();

await Task.Run(
    () => stressTestTask.ApiStressTestTask(_executionCancellationToken.Token),
    _executionCancellationToken.Token);

整个执行从一个ICommand(MVVM)开始:

private RelayCommand _startCommand;
public RelayCommand StartCommand
{
    get
    {
        return _startCommand ?? (_startCommand = new RelayCommand(
            async () =>
            {
                await StartStressTest();
            }));
    }
}

RelayCommand 是库Mvvm-LightICommand 的实现。

我不明白这种行为:如果我用“低”数量的任务(例如 2000)配置我的一批任务,则 GUI 在执行时不会冻结。相反,如果我选择 5000 个任务,那么它会在一段时间后冻结。如果然后我打开我的客户端的另一个 .exe 实例并在每个实例上选择 2000,则 GUI 对两者都有响应。

我的第一个问题是:为什么用x 任务打开一个实例在响应性方面比用x/n 任务打开n 实例更差?是否与 Windows 调度程序有关,以及在第一种情况下我只有一个进程?

我的第二个问题是:我怎样才能解决这个问题,让一切都在一个 GUI 上运行?我想过用单批压力测试制作一个控制台应用程序,并为我想要的每个实例从 GUI 调用一个命令,以便为每批生成一个进程。

【问题讨论】:

  • 您是否使用HttpClient 调用端点?如果是这样,请记住 HttpClient 使用 HttpWebRequest,默认限制为每台主机 2 个并发连接。因此,如果连接数量越多,您的测试客户端的多个实例就会获得更好的总吞吐量。
  • 我正在从 WCF xsd 定义中调用自动生成的客户端。但是你的评论很有趣。

标签: c# wpf windows async-await task


【解决方案1】:

我的第一个问题是:为什么用 x 个任务打开一个实例在响应性方面比用 x/n 个任务打开 n 个实例更差?

可能是因为您要在 UI 线程上处理更多事件。我猜您的 ErrorBetCountChanged、GoodRequestCountChanged 和 TryRequestCountChanged 事件处理程序是在 UI 线程上调用的,并且引发的许多事件可能会淹没 UI 线程。

正如 Gusdor 建议的那样,您可能应该找到一种批量更新的方法。看看响应式扩展 (Rx):http://www.introtorx.com/content/v1.0.10621.0/01_WhyRx.html

它有一个 Buffer 方法可能会派上用场:http://www.introtorx.com/content/v1.0.10621.0/13_TimeShiftedSequences.html

它还具有 en Obervable.FromEvent 方法,可用于将事件转换为 IObservable:https://msdn.microsoft.com/en-us/library/hh229241(v=vs.103).aspx

我的第二个问题是:我怎样才能解决这个问题,让一切都在一个 GUI 上运行?

您需要找到一种方法(一种或另一种)来减少更新 UI 的频率。批处理更新和事件应该是一个很好的起点。减少通知是另一种选择。也许你需要两者。

【讨论】:

    【解决方案2】:

    我怎样才能解决这个问题,让一切都在一个 GUI 上运行?

    仅使用一个线程以“适当的”异步等待方式发送 API 请求。

    private async Task SendStressRequests()
    {
        var tasks = new List<Task>();
        for (int i = 0; i < 4000; i++)
        {
            var task = SendApiRequestAsync();
            tasks.Add(task);
        }
    
        await Task.WhenAll(tasks);
        // Update UI with results
    }    
    

    【讨论】:

      【解决方案3】:

      您是否通过调用 UI 上下文来处理这些 API 事件?如果您有许多调用发生,您将向调度程序充斥着操作,并导致 UI 挂起并落后于用户输入。

      尝试批量更新 UI。

      【讨论】:

      • 如何批量更新 UI?
      • @AZ_ 这个问题的答案不是评论。请开始一个新问题,或者,谷歌是你的朋友google.co.uk/…
      • 兄弟,我整天都在谷歌上搜索!没有找到批处理多个更新的任何解决方案。
      • @AZ_ 如果您发现自己提出了有关该主题的问题,我会为您解答。
      猜你喜欢
      • 2011-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      • 1970-01-01
      • 2017-06-28
      相关资源
      最近更新 更多