【问题标题】:Multiple Async Methods, best to use WhenAll with different return types多个异步方法,最好使用具有不同返回类型的WhenAll
【发布时间】:2015-03-05 13:24:57
【问题描述】:

我有一个异步控制器操作方法,它在我的后端调用 4 个异步方法以从每个方法返回列表。每种方法的对象列表都不同。即列表列表等。

我是这样工作的:

BizProvider bp = new BizProvider();
List<biz.Customer> custReturn = await bp.GetCustomerAsync();
List<biz.Account> acctReturn = await bp.GetAccountAsync();
...plus 2 more
List<object> returnArr = new List<object>();
returnArr.Add(custReturn);
returnArr.Add(acctReturn);  ...plus 2 more
return JsonConvert.SerializeObject(returnArr);

由于有多个任务,我应该使用Task.WhenAll 吗?

微软的例子 https://msdn.microsoft.com/en-us/library/hh194874%28v=vs.110%29.aspx

所有任务都返回List&lt;int&gt;

所以我使用了抽象类,并让我的所有列表对象类型都继承自它。 我更改了业务对象提供者的返回类型以返回抽象类型列表 所以现在我可以做:

var tasks1 = new List<Task<List<Biz.AbstractClass>>>();
tasks1.Add(bp.GetCustomerAsAbstractAsync());
tasks1.Add(bp.GetAccountAsAbstractAsync());
...plus 2 more

然后我打电话给 var continuation = Task.WhenAll(tasks1);

这会执行,然后下一行到达.Results,但随后停止执行???

foreach (var result in continuation.Result)
                                       ^ stops here

我再打电话

returnArrays.Add(result1);
return JsonConvert.SerializeObject(returnArrays);

但这些永远不会被击中......我不知道为什么。 也许我不需要WhenAll,但我仍然很好奇出了什么问题。 也许我需要等待特定函数,或者以某种方式调用 Action,因为在 Microsoft 链接中,这些函数被称为内联。

Task.Run(async () => { x=x,etc...)

编辑 20150306 => 添加更多实现细节

CustMan cm = new CustMan(); 
List<object> returnArr = new List<object>();
var aTask = cm.GetCustomersAsync(); 
var bTask = cm.GetAccountsAsync(); 
await Task.WhenAll(aTask, bTask);  
returnArr.Add(aTask.Result);//same for bTask <-- breakpoint never hits 
return JsonConvert.SerializeObject(returnArr); 
//also .js ajax return method never comes back.

//in CustMan()
public async Task<List<biz.Customer>> GetCustomersAsync() {
    List<biz.Customer> custList = await (from contact in ObjectContextDb.GetData<da.ContactInfo>()//<--generic returns IQueryable
     join customerContact in ObjectContextDb.GetData<da.CustomerContact>() on contact.Id equals customerContact.ContactInfoID
     join customerOrg in ObjectContextDb.GetData<da.CustomerOrganisation>() on customerContact.OrgID equals customerOrg.Id
     orderby contact.LastName
     select new biz.Customer {
       CustomerContactInfo = new biz.Contact() {
         ID = contact.Id,
         WorkPhone = contact.WorkPhone
       },
       CustomerOrg = new biz.CustomerOrganisation {
         ID = facultyOrg.Id,
         Name = facultyOrg.OrgName,
         ClientID = (customerContact.ClientID.HasValue ? customerContact.ClientID.Value : 0)
       }
     }).ToListAsync<biz.Customer>();
    return custList;// <-- Breakpoint hits here, the List has items
}

public async Task<List<biz.Account>> GetAccountsAsync()
{
 var roles = (from acctType in ObjectContextDb.GetData<da.AccountInType>()
  join r in ObjectContextDb.GetData<da.AccountType>() on acctType.AccountTypeID equals r.ID
  select new
  {
   AccountId = acctType.AccountID,
   Type = r.TypeName
  });   //.ToList();

  List<biz.Account> allContacts = await (from account in ObjectContextDb.GetData<da.Account>()
       orderby account.Name
       select new biz.Account()
       {
         Number = account.Id,
         Name = account.Name,
         Roles = (from r in roles where account.Id == r.AccountId select r.Type).ToList()
       }).ToListAsync<biz.Account>();

  return allContacts;//  <-- Breakpoint hits here, the List has items
}

不带WhenAll调用Manager类方法的方法有效!

CustMan cm = new CustMan(); 
List<object> returnArrays = new List<object>();
List<biz.Customer> custReturn = await cm.GetCustomersAsync();
List<biz.Account> acctReturn = await cm.GetAccountsAsync();

returnArrays.Add(custReturn);
returnArrays.Add(acctReturn);

return JsonConvert.SerializeObject(returnArrays);

【问题讨论】:

  • 您可以调用 .GetAllFacultyAsync().GetAllContactsWithRolesAsync() 并稍后等待它们。这将启动所有异步请求,但稍后等待它们,使它们保持强类型。

标签: c# .net asynchronous async-await


【解决方案1】:

您可以使用Task.WhenAll 而无需更改任何内容。只需将任务存储在一个变量中,然后添加它们的结果:

var aTask = GetAAsync();
var bTask = GetBAsync();
...
await Task.WhenAll(aTask, bTask);
returnArr.Add(aTask.Result);
returnArr.Add(bTask.Result);  

但是,在您的情况下,异步操作使用 doesn't support multiple operations concurrently 的实体框架,因此您不能这样做。

一次调用并等待您的操作:

returnArr.Add(await GetAAsync());
returnArr.Add(await GetBAsync());  
...

【讨论】:

  • 我试过这个,但是如果我在 await 之后放置一个断点,它永远不会被命中,并且命中调用 this 的 action 方法的 ajax 调用永远不会返回。
  • @magister 那么你的任务永远不会完成。显示他们的代码。
  • var aTask = cm.GetCustomersAsync(); var bTask = cm.GetAccountsAsync(); //以上 CustMan cm = new CustMan();列表 returnArr = 新列表();等待 Task.WhenAll(aTask, bTask); returnArr.Add(aTask.Result);//bTask
  • @magister 那么你的异步方法永远不会完成。显示他们的代码,我们可以告诉你原因。
  • @magister EF 不支持并发操作。不要使用Task.WhenAllawait每个操作分开。
【解决方案2】:

听起来你正在这样做:

var continuation = Task.WhenAll(tasks1);
foreach (var result in continuation.Result) { }

这可能是deadlockingWhenAll 也返回一个 Task,所以你需要 await 那个:

var results = await Task.WhenAll(tasks1);
foreach (var result in results) { }

否则,构建一个List 的任务并将它们传递给WhenAll 是同时等待多个任务的完全合理的方式。

【讨论】:

  • 哦,天哪,我错过了
  • 你知道如果我在 foreach 之后放置一个断点......它会被击中吗?我到了这一点,没有例外,但后来我被推到 global.asax.cs 中的 Acquire_RequestState
猜你喜欢
  • 1970-01-01
  • 2019-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-10
  • 2019-04-05
  • 1970-01-01
相关资源
最近更新 更多