【问题标题】:Thread management with ASP.NET async/await使用 ASP.NET async/await 进行线程管理
【发布时间】:2014-11-14 16:55:59
【问题描述】:

我有一个数据库实体类型Entity,一长串Thingy 和方法

private Task<Entity> MakeEntity(Thingy thingy) {
  ...
}

MakeEntity 做了很多事情,并且受 CPU 限制。我想将我所有的东西转换为实体,并将它们保存在 db.context 中。考虑到

  • 我不想尽快完成
  • 实体量大,想有效利用数据库,所以想开始保存更改,等待远程数据库做的事

我怎样才能高效地做到这一点?我真正想要的是在等待数据库执行它的操作时循环,并提供到目前为止所有新创建的实体,直到数据库处理完它们。那里最好的路线是什么?如果同时调用它,我会遇到 saveChanges 抛出,所以我不能这样做。我真正想要的是有一个由八个线程(或者更确切地说,与我拥有的内核一样多的线程)组成的线程池来执行 CPU 绑定的工作,以及一个执行 SaveChanges()

的线程

【问题讨论】:

  • 您确定想要在 ASP.NET 上使用多线程吗?请记住,当单个请求使用多个线程时,这将显着影响您的可伸缩性,因为这些线程不能用于其他请求。
  • “一长串Thingy”。您是否对每个 Thingy 实例重复调用 MakeEntity
  • 另外,有没有办法批量处理一堆Thingys 的 CPU 绑定处理并将生成的 Entitys 附加到上下文中,然后 @987654329 @ 仅用于一次 DB 往返?
  • @Asad MakeEntity 应该为每个东西调用,是的(但我的问题实际上是如何的一部分。为整个批次调用一次 SaveChanges 是迄今为止最好的,但由于所需的工作量我要避免的主要事情是MakeEntity 的独立工作并等待SaveChanges 的中间结果,同时注意没有多个SaveChanges 同时“飞行中”,因为EF 不支持。

标签: c# multithreading entity-framework asynchronous async-await


【解决方案1】:

这是一种“异步流”,总是有点别扭。

在这种情况下(假设您真的确实想在 ASP.NET 上使用多线程,一般不推荐这样做),我会说TPL Dataflow 是您的最佳选择。您可以将TransformBlockMaxDegreeOfParallelism set to 8 (or unbounded, for that matter) 一起使用,并将其链接到执行SaveChangesActionBlock

请记住,对 CPU 密集型代码使用同步签名(不是 async/await),对 I/O 密集型代码使用异步方法(即SaveChangesAsync)。

【讨论】:

    【解决方案2】:

    您可以设置一个由 N 个 CPU 工作人员组成的管道,这些工作人员向数据库工作人员提供数据。数据库工作者可以批量处理项目。

    由于MakeEntity 受CPU 限制,因此无需在此处使用asyncawaitawait 不创建任务或线程(一种常见的误解)。

    var thingies = ...;
    var entities = thingies.AsParallel().WithDOP(8).Select(MakeEntity);
    var batches = CreateBatches(entities, batchSize: 100);
    
    foreach (var batch in batches) {
     Insert(batch);
    }
    

    您需要提供一种从IEnumerable 创建批次的方法。这可以在网络上找到。

    如果您不需要对数据库部分进行批处理,您可以删除该代码。

    对于数据库部分你可能don't need async IO,因为它似乎是一个低频操作。

    【讨论】: