我有一个对象列表,我需要运行一个长时间运行的进程,我想异步启动它们,然后当它们全部完成时将它们作为列表返回给调用方法。我一直尝试使用不同的方法,但是看起来这些进程仍按照它们在列表中的顺序同步运行。所以我确信我在如何执行任务列表的过程中遗漏了一些东西。 这是我的代码:
public async Task<List<ShipmentOverview>> GetShipmentByStatus(ShipmentFilterModel filter)
{
if (string.IsNullOrEmpty(filter.Status))
{
throw new InvalidShipmentStatusException(filter.Status);
}
var lookups = GetLookups(false, Brownells.ConsolidatedShipping.Constants.ShipmentStatusType);
var lookup = lookups.SingleOrDefault(sd => sd.Name.ToLower() == filter.Status.ToLower());
if (lookup != null)
{
filter.StatusId = lookup.Id;
var shipments = Shipments.GetShipments(filter);
var tasks = shipments.Select(async model => await GetOverview(model)).ToList();
ShipmentOverview[] finishedTask = await Task.WhenAll(tasks);
return finishedTask.ToList();
}
else
{
throw new InvalidShipmentStatusException(filter.Status);
}
}
private async Task<ShipmentOverview> GetOverview(ShipmentModel model)
{
String version;
var user = AuthContext.GetUserSecurityModel(Identity.Token, out version) as UserSecurityModel;
var profile = AuthContext.GetProfileSecurityModel(user.Profiles.First());
var overview = new ShipmentOverview
{
Id = model.Id,
CanView = true,
CanClose = profile.HasFeatureAction("Shipments", "Close", "POST"),
CanClear = profile.HasFeatureAction("Shipments", "Clear", "POST"),
CanEdit = profile.HasFeatureAction("Shipments", "Get", "PUT"),
ShipmentNumber = model.ShipmentNumber.ToString(),
ShipmentName = model.Name,
};
var parcels = Shipments.GetParcelsInShipment(model.Id);
overview.NumberParcels = parcels.Count;
var orders = parcels.Select(s => WareHouseClient.GetOrderNumberFromParcelId(s.ParcelNumber)).ToList();
overview.NumberOrders = orders.Distinct().Count();
//check validations
var vals = Shipments.GetShipmentValidations(model.Id);
if (model.ValidationTypeId == Constants.OrderValidationType)
{
if (vals.Count > 0)
{
overview.NumberOrdersTotal = vals.Count();
overview.NumberParcelsTotal = vals.Sum(s => WareHouseClient.GetParcelsPerOrder(s.ValidateReference));
}
}
return overview;
}
答案 0 :(得分:1)
看起来你正在使用异步方法而你真的想要线程。
Asynchronous methods在调用async
方法时将控制权返回给调用方法,然后等到方法在await
上完成。你可以看到它是如何运作的here
基本上,async / await方法的唯一用处是不要锁定UI,以便它保持响应。
如果您要并行触发多个处理,则需要使用threads,如下所示:
using System.Threading.Tasks;
public void MainMethod() {
// Parallel.ForEach will automagically run the "right" number of threads in parallel
Parallel.ForEach(shipments, shipment => ProcessShipment(shipment));
// do something when all shipments have been processed
}
public void ProcessShipment(Shipment shipment) { ... }
答案 1 :(得分:0)
将方法标记为async
并不会自动神奇地使其并行执行。由于您根本不使用await
,因此它实际上会完全同步执行,就好像它不是async
一样。您可能已经读过async
使函数异步执行的某个地方,但这根本不是真的 - 忘了它。它唯一能做的就是构建一个状态机来处理任务延续,当你使用await
并实际构建所有代码来管理这些任务及其错误处理时。
如果您的代码主要是I / O绑定,请使用await
的异步API来确保方法实际并行执行。如果它们受CPU限制,Task.Run
(或Parallel.ForEach
)将最有效。
此外,.Select(async model => await GetOverview(model)
没有任何意义。它几乎相当于.Select(model => GetOverview(model)
。在任何情况下,由于该方法实际上没有返回异步任务,因此在执行Select
之前,它将在执行Task.WhenAll
之前执行。
鉴于此,即使GetShipmentByStatus
&#39; async
也毫无用处 - 您只需使用await
等待Task.WhenAll
,但由于所有任务已经完成了这一点,它将简单地同步完成。
答案 2 :(得分:0)
如果你的任务是CPU限制而不是I / O限制,那么这就是我认为你正在寻找的模式:
static void Main(string[] args) {
Task firstStepTask = Task.Run(() => firstStep());
Task secondStepTask = Task.Run(() => secondStep());
//...
Task finalStepTask = Task.Factory.ContinueWhenAll(
new Task[] { step1Task, step2Task }, //more if more than two steps...
(previousTasks) => finalStep());
finalStepTask.Wait();
}