繁体   English   中英

如果没有要完成的任务,则Task.WhenAll会挂断

[英]Task.WhenAll hangs out if there are no tasks to complete

我有将对象列表插入Mongo DB的方法。

public class StorageService : IStorageService
{
  public Task<BulkWriteResult<Option>> SaveOptions(List<Option> contracts)
  {
    var context = new MongoContext<Option>();
    return context.SaveCollection(contracts);
  }
}

var optionIds = Task
  .WhenAll(storageService.SaveOptions(optionDetails.Values.ToList()))
  .Result;

如果合同列表为空,则没有对象要插入数据库,也没有要完成的任务,因此Task.WhenAll会无限期地运行,从而产生死锁。

如果列表为空,是否有一种方法可以返回空/已完成的任务,或者也许有一个更好的解决方案来获取插入结果,但是同时,正确处理没有结果的情况?

更新#1

近似的结构。

WebApi-MVC项目

[AcceptVerbs("POST")]
public Response<int> DownloadOptions([FromBody] ContractSelector data)
{
  .. some controller code

  var optionIds = Task
    .WhenAll(storageService.SaveOptions(optionDetails.Values.ToList()))
    .Result;

  // this method should gather data from multiple APIs
  // so I need Task.Result of all previous operations
  // I could make this method async and use await, but it's not the case here
}

.NET 4.6.1类库项目

public class StorageService : IStorageService
{
  public Task<BulkWriteResult<Option>> SaveOptions(List<Option> contracts)
  {
    var context = new MongoContext<Option>();
    return context.SaveCollection(contracts);
  }
}

更新#2

为什么没有用于异步/等待。 有2个外部API调用,一个是获取有关某种资产的一般信息,第二个是获取该资产的价格,我无法更改。 因此,如果我想以一种方法获取所有信息,则必须请求常规信息,然后等待 Result ,然后根据常规信息请求相关价格。 之后,我想将收集到的信息保存到数据库中,并在对API的响应中返回已保存ID的列表。

通话顺序

1. UI
2. WebApi MVC Controller
3. Class Library
3.1 Request asset info - wait for the result
3.2 Get asset info - request prices for selected assets - wait for the result
3.3 Get asset info and prices info - save everything to DB - return response

var contracts = Task.WhenAll(optionService.GetContracts(params)).Result;
var prices = Task.WhenAll(optionService.GetOptionDetails(contracts)).Result;
var ids = Task.WhenAll(storageService.SaveOptions(prices.Values.ToList()));

因此,响应取决于3.3,3.3取决于3.2,3.2取决于3.1。 如果您知道如何将其全部转为非阻塞呼叫,那么我无所不能。 目前,我认为1个HTTP请求中的3个阻塞调用要比3个单独的异步HTTP请求更好。

无法详细解释,但问题似乎是由.NET Framework和Mongo DB驱动程序之间不兼容引起的。 目前,我正在使用.NET 4.6.1和Mongo 2.6.1,并且工作正常。

昨天,任何试图将一个空对象列表保存到Mongo DB中的尝试都导致了死锁。

然后,我尝试重构代码并安装最新版本的Mongo DB,即2.7.0。 现在,任何使用Mongo DB的CRUD操作都将返回此错误。 没有调用堆栈,有关此异常的更多详细信息。

MethodAccessException: Attempt by method 'MongoDB.Driver.ClientSession..ctor(MongoDB.Driver.IMongoClient, MongoDB.Driver.ClientSessionOptions, MongoDB.Driver.IServerSession, Boolean)' to access method 'MongoDB.Driver.Core.Clusters.ClusterClock..ctor()' failed.

然后我尝试将Mongo DB的版本降低到2.6.1,并且当我尝试插入空对象列表时,我得到了这个异常,这是正确的。 当我插入至少一条记录时,它会按预期工作。

System.AggregateException: 'One or more errors occurred.'
ArgumentException: Must contain at least 1 request.
Parameter name: requests

测试代码

var item = new Demo
{
  Symbol = "SomeSymbol",
  Expiration = "2019-01-01"
};

var list = new List<Demo>();

list.Add(item); 
list.Add(item); // if I comment these lines, then Mongo 2.6.1 returns correct exception

var optionIds = Task.WhenAll(storageService.Save(list)).Result;

保存方法是Mongo存储库模式的一部分

public Task<BulkWriteResult<T>> SaveCollection(List<T> items)
{
  var records = new List<ReplaceOneModel<T>>();
  var processes = new List<Task<ReplaceOneResult>>();

  items.ForEach(contract =>
  {
    var record = new ReplaceOneModel<T>(Builders<T>
      .Filter
      .Where(o => o.Id == contract.Id), contract)
    {
      IsUpsert = true
    };

    records.Add(record);
  });

  return Collection.BulkWriteAsync(records);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM