0

我正在迈出并行编程的第一步。我将CalculateSlots 重写为CalculateSlotsAsync。它看起来可以正常工作(快 3 倍)。

我的问题是:写对了吗?我是否需要使用最新的异步离开模式,如果是,如何?

private void CalculateSlots(bool isCalculateAllSlots)
{
    foreach (IndicatorSlot indicatorSlot in strategy.Slot)
    {
        if (isCalculateAllSlots || !indicatorSlot.IsCalculated)
            CalculateStrategySlot(indicatorSlot.SlotNumber);
    }
}

private void CalculateSlotsAsync(bool isCalculateAllSlots)
{
    var tasks = new List<Task>();
    foreach (IIndicatorSlot indicatorSlot in strategy.Slot)
    {
        if (isCalculateAllSlots || !indicatorSlot.IsCalculated)
        {
            IIndicatorSlot slot = indicatorSlot;
            Task task = Task.Factory.StartNew(() => CalculateStrategySlot(slot.SlotNumber));
            tasks.Add(task);
        }
    }
    Task.WaitAll(tasks.ToArray());
}

在 i7-3630QM @2.40Gh 上测试

// Executed for 96 sec. 
for (int i = 0; i < 1000; i++)
    CalculateSlots(true);

// Executed for 34 sec. 
for (int i = 0; i < 1000; i++)
    CalculateSlotsAsync(true);
4

1 回答 1

3

对于数据并行操作,您通常可以使用 PLINQ 来简化实现:

strategy.Slot.AsParallel()
             .Where(slot => isCalculateAllSlots || !indicatorSlot.IsCalculated)
             .ForAll(slot => CalculateStrategySlot(slot.SlotNumber));

但是,在您的情况下,每个项目都需要相对较长的时间来计算,所以我建议将它们保留为任务但将它们标记为LongRunning(这通常具有在专用线程上执行它们的效果,而不是线程池)。

Task task = Task.Factory.StartNew(() => CalculateStrategySlot(slot.SlotNumber),
                                  TaskCreationOptions.LongRunning);

回复Task.WaitAll导致调用线程(在您的情况下为 UI 线程)阻塞,直到所有指定的任务都完成。( PLINQ 的行为类似ForAll。)

为了让您的 UI 保持响应,您需要从阻塞方法切换到异步方法。例如,假设您有:

Task.WaitAll(tasks.ToArray());
UpdateUI(strategy.Slot);    // must be called on UI thread

您可以将其替换为:

Task.Factory.ContinueWhenAll(tasks.ToArray(), completedTasks =>
{
    // callback on UI thread
    UpdateUI(strategy.Slot);
},
    CancellationToken.None,
    TaskContinuationOptions.None,
    TaskScheduler.FromCurrentSynchronizationContext());

在实践中,您还需要学习如何使用CancellationToken以允许用户在操作完成之前放弃操作。

于 2013-07-05T22:04:45.293 回答