2

我有一个任务工厂,它启动了许多任务,有时超过 1000 个。我将每个任务添加到列表中,并在任务完成后将其删除。

var scan = Task.Factory.StartNew(() =>
            {
                return operation.Run();
            }, token.Token
            );

            operations.Add(scan);

当任务完成时:

var finishedTask = scan.ContinueWith(resultTask =>
                OperationComplete(resultTask),
                TaskContinuationOptions.OnlyOnRanToCompletion
                );

public virtual void OperationComplete(Task task)
    {
        operations.Remove(task);
    }

全部完成后:

    Task.Factory.ContinueWhenAll(operations.ToArray(),
         result =>
         {
             AllOperationsComplete();
         }, TaskContinuationOptions.None);

然后,在我的应用程序中的某些点,我想获得正在运行的任务的计数。(这是我得到错误的地方:“集合已修改;枚举操作可能无法执行。”)

    public int Count()
    {  
        int running = operations.Count<Task>((x) => x.Status == TaskStatus.Running);
        return running;
    }

几个问题:

1)我是否应该担心从列表中删除任务?该列表很容易达到 1000 多个。

2) 使 Count() 安全的最佳方法是什么?如果我没记错的话,创建一个新列表并添加operations到它仍然会枚举集合。

4

4 回答 4

4

您需要锁定以确保一次只有一个线程访问列表(无论是在删除还是计数期间),或者您应该使用并发集合。不要忘记Count(Func<T, bool>)需要迭代集合才能执行计数 - 这就像使用foreach循环......并且在迭代集合时(通常)不能修改集合。

我怀疑这ConcurrentBag是一个合适的选择——当你使用 TPL 时,大概你有可用的 .NET 4 并发集合......

于 2012-08-05T19:01:52.357 回答
1

您需要确保在迭代时不修改集合。大多数收藏不支持这一点。Alock可能就足够了。但是,您可能需要重新审视设计。长时间锁定集合可能会扼杀您希望从异步任务中获得的任何性能提升。

于 2012-08-05T19:01:50.590 回答
0

鉴于代码已经作为计数调用的一部分检查状态,并且假设您在所有任务都在集合中之后才进行计数,只是不删除它们似乎是最简单的答案。如果您决定将 List 换成其他东西,请确保实际测量性能差异,尤其是当 Count 调用的执行次数相对于集合的大小而言较低时。:)

于 2012-08-06T00:41:26.533 回答
0

您可以使用ConcurrentDictionary 来跟踪您的任务(Concurrentbags 不允许您删除特定项目)。

ConcurrentDictionary<Task, string> runningTasks = new ConcurrentDictionary<Task, string>();

Task task = Task.Factory.StartNew(() =>
    {
        // Do your stuff
    }).ContinueWith(processedTask => {
        var outString; // A string we don't care about
        runningTasks.TryRemove(processedTask, out outString);
    });

runningTasks.TryAdd(task, "Hello I'm a task");
// Add lots more tasks to runningTasks

while (runningTasks.Count > 0)
{
     Console.WriteLine("I'm still waiting...");
     Thread.Sleep(1000);
}

如果你想做一个正确的“ WaitAll”(需要 LINQ):

    try
    {
        Task[] keys = runningTasks.Keys.Select(x => x).ToArray();
        Task.WaitAll(keys);
    }
    catch { } // WaitAll will always throw an exception.

希望能帮助到你。

于 2014-01-26T02:51:45.710 回答