如何在c#中正确地异步执行任务列表
本文关键字:异步 执行任务 列表 正确地 | 更新日期: 2023-09-27 18:06:38
我有一个对象列表,我需要在上面运行一个长时间运行的进程,我想异步地启动它们,然后当它们全部完成时,将它们作为列表返回给调用方法。我一直在尝试我发现的不同方法,但是似乎这些进程仍然按照它们在列表中的顺序同步运行。所以我确信在如何执行任务列表的过程中我遗漏了一些东西。下面是我的代码:
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;
}
看起来你在使用异步方法,而你真正想要的是线程。
异步方法在调用async
方法时将控制权交还给调用方法,然后等待直到await
上的方法完成。你可以在这里看到它是如何工作的。
基本上,async/await方法的唯一用途是不锁定UI,以便它保持响应。
如果您想并行地触发多个处理,您将希望使用线程,如:
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) { ... }
将方法标记为async
不会自动神奇地使其并行执行。由于您根本没有使用await
,因此它实际上将完全同步执行,就好像它不是async
一样。您可能在某处读到过async
使函数异步执行,但这根本不是真的-忘记它吧。当你使用await
时,它所做的唯一一件事就是为你构建一个状态机来处理任务的延续,并实际构建所有的代码来管理这些任务和它们的错误处理。
如果你的代码主要是I/O绑定,使用异步api与await
,以确保方法实际上并行执行。如果它们是CPU限制的,Task.Run
(或Parallel.ForEach
)将工作得最好。
同样,做.Select(async model => await GetOverview(model)
也没有意义。它几乎等同于.Select(model => GetOverview(model)
。在任何情况下,由于该方法实际上不返回异步任务,因此它将在执行Select
时执行,远早于执行Task.WhenAll
。
考虑到这一点,即使GetShipmentByStatus
的async
也几乎毫无用处-您只使用await
来等待Task.WhenAll
,但由于所有任务都已经完成,因此它将简单地同步完成。
如果您的任务是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();
}