【问题标题】:Is my code really executed asynchronously?我的代码真的是异步执行的吗?
【发布时间】:2020-03-12 22:10:49
【问题描述】:

列表中有几个对象。每分钟,对于他们每个人,我都必须下载api信息并将接收到的数据保存到数据库中。

列表中的对象相互独立。每个对象的方法都可以单独调用。根据 api 的响应时间,单个方法的总执行时间通常在 ~0.5 到 5 秒之间。

按照stackoverflow上其他线程的建议,我创建了以下代码:

private async void DownloadAndSaveDataForMyObjects(object state)
{
   try
   {
      await Task.Run(() => Parallel.ForEach(MyObjectsList, ServiceSingleObjectMethod));
   }
}


private void ServiceSingleObjectMethod(RehDeviceSmsRequest myObjectFromList)
{
   var apiInfo = GetInfoFromApi(myObjectFromList);
   SaveInfoToDatabase(myObjectFromList);
}

在我看来,代码是异步的。但是……我有些怀疑。记录器通知我在应用程序运行期间执行的操作。对象的方法调用之间的间隔非常大。请看这个:

[13:32:46 DBG][MyService]->MyMethod => Update object id: 54
[13:32:47 DBG][MyService]->MyMethod => Update object id: 9
[13:32:50 DBG][MyService]->MyMethod => Update object id: 47
[13:32:51 DBG][MyService]->MyMethod => Update object id: 21
[13:32:51 DBG][MyService]->MyMethod => Update object id: 53
[13:32:53 DBG][MyService]->MyMethod => Update object id: 37
[13:32:55 DBG][MyService]->MyMethod => Update object id: 62
[13:32:55 DBG][MyService]->MyMethod => Update object id: 63
[13:32:56 DBG][MyService]->MyMethod => Update object id: 64
[13:32:56 DBG][MyService]->MyMethod => Update object id: 55
[13:32:56 DBG][MyService]->MyMethod => Update object id: 36
[13:32:56 DBG][MyService]->MyMethod => Update object id: 30
[13:32:56 DBG][MyService]->MyMethod => Update object id: 46
[13:32:56 DBG][MyService]->MyMethod => Update object id: 26
[13:32:56 DBG][MyService]->MyMethod => Update object id: 56
[13:33:00 DBG][MyService]->MyMethod => Update object id: 29
[13:33:00 DBG][MyService]->MyMethod => Update object id: 57
[13:33:00 DBG][MyService]->MyMethod => Update object id: 42
[13:33:01 DBG][MyService]->MyMethod => Update object id: 38
[13:33:01 DBG][MyService]->MyMethod => Update object id: 32
[13:33:01 DBG][MyService]->MyMethod => Update object id: 48
[13:33:01 DBG][MyService]->MyMethod => Update object id: 40
[13:33:01 DBG][MyService]->MyMethod => Update object id: 31
[13:33:01 DBG][MyService]->MyMethod => Update object id: 49
[13:33:02 DBG][MyService]->MyMethod => Update object id: 58
[13:33:02 DBG][MyService]->MyMethod => Update object id: 9
[13:33:02 DBG][MyService]->MyMethod => Update object id: 33
[13:33:02 DBG][MyService]->MyMethod => Update object id: 41
[13:33:04 DBG][MyService]->MyMethod => Update object id: 50
[13:33:04 DBG][MyService]->MyMethod => Update object id: 34
[13:33:06 DBG][MyService]->MyMethod => Update object id: 17
[13:33:06 DBG][MyService]->MyMethod => Update object id: 59
[13:33:07 DBG][MyService]->MyMethod => Update object id: 45
[13:33:08 DBG][MyService]->MyMethod => Update object id: 61
[13:33:08 DBG][MyService]->MyMethod => Update object id: 54
[13:33:08 DBG][MyService]->MyMethod => Update object id: 43
[13:33:08 DBG][MyService]->MyMethod => Update object id: 51
[13:33:08 DBG][MyService]->MyMethod => Update object id: 52

处理后续对象的间隔相当大。我认为我的异步代码会使每个对象的方法同时被调用(或者非常非常相似)......

如何确保我的代码是异步的并且可以正常工作? 你能确认我正确地为每个对象调用任务吗?

【问题讨论】:

  • 您是否尝试记录每个并行任务的线程 ID (Thread.CurrentThread.ManagedThreadId)?
  • 你使用的是哪个SynchronizationContext
  • 我看到六个日志条目的时间为“13:33:01”....我不知道这是什么意思,但它似乎符合“每个对象的方法同时被调用”的标准。
  • 请澄清您是否在问题中使用“异步”一词作为“并行运行”。请注意,asyncParallel.ForEach 都没有保证 all 操作将并行运行(ForEach 实际上几乎可以保证,因为它使用自己的内部策略并行运行迭代)
  • 正如迈克尔兰德尔在他的出色回答中提到的那样,Parallel.ForEach 并不是您真正想要的。如果您真的想为每个项目启动一个线程,请创建一个List<Task>,循环您的MyObjectList 并将Task.Run 任务添加到循环内的List。然后你可以await Task.WhenAll(taskList);你会注意到你的输出有所不同。

标签: c# asynchronous parallel.foreach


【解决方案1】:

Parallel.ForEach 不会主动从线程池中释放线程。 任务调度程序将决定你应该使用多少线程,并且根据内核、你的系统已经在做什么以及它从你的任务中收集到的启发式方法,它可能不会做你想做的事情预计。

但是,这里有更大的概念问题。您正在使用Parallel.ForEach(看起来是)面包和黄油IO 绑定工作,它正在捆绑/阻塞线程,可以更适合卸载到IO 完成端口,进而有更大的机会实现更多并发 IO Bound 工作负载

简而言之,Parallel.ForEach 不适合 IO 绑定的工作负载,它针对 CPU 绑定的工作负载进行了优化,并且不支持 async 和 await 模式。

总而言之,您应该让您的 IO 方法为 async(一直向下)并使用更合适的技术来处理 async 和await 模式,例如 Task.WhenAllTPL DataFlow ActionBlock<T>Reactive Extensions。此外,您可能应该将您的工作批处理到 数据库,这样您也不会受到影响。这将使您的 工作负载 最大可能不会阻塞 线程池 线程,反过来您可能会发现 吞吐量 em>(通常)会高得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多