【问题标题】:Asynchronous Tasks take too much time异步任务花费太多时间
【发布时间】:2015-05-27 11:50:58
【问题描述】:

我一直在尝试对我的 CPU 绑定函数采用异步方法来计算一些聚合函数。问题是存在一些死锁(我想),因为计算的时间太不同了。我是这个任务并行世界的真正新手,我也阅读了 Stephem Cleary 的文章,但我仍然不确定这种异步方法的所有方面。 我的代码:

private static void Main(string[] args)
{
    PIServer server = ConnectToDefaultPIServer();
    AFTimeRange timeRange = new AFTimeRange("1/1/2012", "6/30/2012");
    Program p = new Program();
    for (int i = 0; i < 10; i++)
    {
        p.TestAsynchronousCall(server, timeRange);
        //p.TestAsynchronousCall(server, timeRange).Wait();-same results
    }
    Console.WriteLine("Main check-disconnected done");
    Console.ReadKey();
}

private async Task TestAsynchronousCall(PIServer server, AFTimeRange timeRange)
{
    AsyncClass asyn;
    for (int i = 0; i < 1; i++)
    {
        asyn = new AsyncClass();
        await asyn.DoAsyncTask(server, timeRange);
        //asyn.DoAsyncTask(server, timeRange);-same results
    }
}

public async Task DoAsyncTask(PIServer server, AFTimeRange timeRange)
{
        var timeRanges = DivideTheTimeRange(timeRange);
        Task<Dictionary<PIPoint, AFValues>>[] tasksArray = new Task<Dictionary<PIPoint, AFValues>>[2];

        tasksArray[0] = (Task.Run(() => CalculationClass.AverageValueOfTagPerDay(server, timeRanges[0])));
        // tasksArray[1] = tasksArray[0].ContinueWith((x) => CalculationClass.AverageValueOfTagPerDay(server, timeRanges[1]));
        tasksArray[1] = (Task.Run(() => CalculationClass.AverageValueOfTagPerDay(server, timeRanges[1])));


        Task.WaitAll(tasksArray);
        //await Task.WhenAll(tasksArray); -same results
        for (int i = 0; i < tasksArray.Length; i++)
        {
            Program.Show(tasksArray[i].Result);
        }
}

我在 AverageValueOfTagPerDay 函数中通过秒表测量时间。这个函数是同步的(有问题吗?)。每个任务需要 12 秒。但是当我取消注释该行并使用 ContinueWith() 方法时,这些任务每个需要 5-6 秒(这是可取的)。怎么可能?

更奇怪的是,当我将 Main() 中的 for 循环设置为 10 时,有时需要 5 秒以及使用 ContinueWith() 时。所以我猜某个地方出现了死锁,但我找不到。

对不起英文,当我尝试解释一些困难时,我仍然遇到问题。

【问题讨论】:

  • 您确实意识到您的第一个 for next 将按顺序执行每个迭代。为什么不使用 forparallel ?
  • DoAsyncTask 不完整:try 没有catch 也没有finally。它被标记为async,但不使用await。你有多个任务使用PIServer 的同一个实例,这个类线程安全吗?
  • 所以它甚至无法编译。 AsyncTask 不必是异步的。
  • Phillip Stuyck:Main 和 TestAsynchronousCall 中的 for 循环仅用于测试目的——我需要比较每个调用的计算时间。此外,尝试捕获只是因为我忘记删除它。 DoAsyncTask 必须是异步的,因为在我运行 2 个任务之后,我必须等待输入到 Program.Show() 方法的结果。
  • Guillaume:关于 D​​oAsyncTask 当我删除关键字 async 并将返回类型更改为 void 时,我得到了相同的计算结果。但是感谢您的建议,我没有意识到这一点。此方法是异步的,因为我尝试使用关键字 await(注释行)。关于 PiServer 的好点,我会检查一下!

标签: c# asynchronous async-await task-parallel-library task


【解决方案1】:

我一直在尝试对我的 CPU 绑定函数采用异步方法来计算一些聚合函数。

“异步”和“CPU 密集型”不是一起使用的术语。如果您有一个 CPU 密集型进程,那么您应该使用并行技术(Parallel、Parallel LINQ、TPL Dataflow)。

我是这个任务并行世界的真正新手,我也阅读过 Stephem Cleary 的文章,但我仍然不确定这种异步方法的所有方面。

可能是因为我没有在我的任何文章或博客文章中介绍并行技术。 :) 我确实在我的书中介绍了它们,但不在网上。我的在线工作侧重于异步,这是基于 I/O 的操作的理想选择。

要解决您的问题,您应该使用并行方法:

public Dictionary<PIPoint, AFValues>[] DoTask(PIServer server, AFTimeRange timeRange)
{
  var timeRanges = DivideTheTimeRange(timeRange);
  var result = timeRanges.AsParallel().AsOrdered().
      Select(range => CalculationClass.AverageValueOfTagPerDay(server, range)).
      ToArray();
  return result;
}

当然,这种方法假定PIServer 是线程安全的。它还假设“服务器”类没有进行 I/O;如果有,那么 TPL Dataflow 可能是比 Parallel LINQ 更好的选择。

如果您打算在 UI 应用程序中使用此代码并且不想阻塞 UI 线程,那么您可以像这样异步调用代码:

var results = await Task.Run(() => DoTask(server, timeRange));
foreach (var result in results)
  Program.Show(result);

【讨论】:

  • 谢谢斯蒂芬,我一直认为异步方法不适合 CPU 密集型操作,但是当我读到你 article 时,你写道:所以,问题仍然存在:我应该在哪里使用Task.Run? 使用 Task.Run 来调用 CPU-bound 方法。仅此而已。 我也一直在阅读您的书,因为我对此感到非常兴奋。我的意思是指使用任务的任务并行库。所以我仍然对正确的名字有点困惑。我相信 PiServer 是线程安全的,我会尝试一下,我会告诉你
  • 好吧,我尝试了 AsParallel() 并且得到了完全相同的结果(每个 12 秒),我的目标是这个计算时间低于 6 秒。所以我要看看 TPL 数据流。另外我应该提到,这是一个纯控制台应用程序,并且计算每天平均值的聚合函数在服务器端(在本例中是我的本地服务器)计算。所以我认为它满足 I/O 方法。我的意思是服务器的输出是具体的平均值。顺便说一句,非常感谢您的回复:-)
  • @Alexander_Dracka:如果我理解正确的话,那么 for this process 的操作是 I/O-bound,而不是 CPU-bound。在这种情况下,您可能希望将asyncTask.WhenAll 一起使用。要检查的一件事(如果您的服务器是 ASP.NET)是服务器请求已禁用会话状态。
猜你喜欢
  • 2013-01-04
  • 2020-07-17
  • 2016-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多