如何在C#中正确执行任务列表异步

时间:2015-08-06 15:45:04

标签: c# asynchronous async-await

我有一个对象列表,我需要运行一个长时间运行的进程,我想异步启动它们,然后当它们全部完成时将它们作为列表返回给调用方法。我一直尝试使用不同的方法,但是看起来这些进程仍按照它们在列表中的顺序同步运行。所以我确信我在如何执行任务列表的过程中遗漏了一些东西。 这是我的代码:

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;
    }

3 个答案:

答案 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();
}