c# - 如何防止 Parallel.ForEach 循环在运行时更改任务数?

标签 c# multithreading parallel.foreach

我正在使用 Parallel.ForEach 循环做一些工作,我用 localInit 初始化它,如下所示:

localInit: () => new
{
    foo = new Foo(),
    bars = CreateBars(),
}

根据 MSDN:

localInit, or the function that initializes the thread-local variable. This function is called once for each partition in which the Parallel.ForEach operation executes. Our example initializes the thread-local variable to zero.

所以我尝试那样使用它,但我观察到循环不断终止并创建新任务,这导致频繁调用 localInit。我的选择会适得其反,无法按预期工作。

我想当 Parallel.ForEach 会创建例如四个分区时,它会让它们保持事件状态,直到它遍历所有项目,但事实并非如此。对于包含几千个项目的集合,它会调用 localFinallylocalInit 数次。怎么会这样?

能否以某种方式阻止这种行为?我真的希望节省一些资源,但它并没有真正让我。


这是循环的样子:

var parallelLoopResult = Parallel.ForEach
(
    source: items,
    parallelOptions: parallelOptions,
    localInit: () => new
    {
        foo = new Foo(),
        bars = CreateBars(),
    },
    body: (item, loopState, i, local) =>
    {
        parallelOptions.CancellationToken.ThrowIfCancellationRequested();

        var results = local.bars.Select(x => ...).ToList().

        ....

        return local;
    },
    localFinally: local =>
    {
        local.foo.Dispose();
        lock (aggregateLock)
        {
            ... process transformed bars
        }
    }
);

并行选项:

var parallelOptions = new ParallelOptions
{
    CancellationToken = cancellationTokenSource.Token,
#if DEBUG
    MaxDegreeOfParallelism = 1
    //MaxDegreeOfParallelism = Environment.ProcessorCount
#else
    MaxDegreeOfParallelism = Environment.ProcessorCount
#endif
};

最佳答案

如果我明白the code正确,Parallel.ForEach()重新启动每个 Task每隔几百毫秒。这意味着如果每次迭代都是实质性的(通常应该如此),您将得到很多 Task s,因此有很多电话 localInitlocalFinally .这样做的原因是对于同一进程中也使用相同 ThreadPool 的其他代码的公平性。 .

我认为没有办法改变 Parallel.ForEach() 的这种行为.我认为解决这个问题的一个好方法是编写自己的简单版本 Parallel.ForEach() .考虑到您可以利用 Partitioner<T>并取决于 Parallel.ForEach() 的哪些功能你需要,它可能相对简单。例如,像这样的东西:

public static void MyParallelForEach<TSource, TLocal>(
    IEnumerable<TSource> source, int degreeOfParallelism,
    Func<TLocal> localInit, Func<TSource, TLocal, TLocal> body, Action<TLocal> localFinally)
{
    var partitionerSource = Partitioner.Create(source).GetDynamicPartitions();

    Action taskAction = () =>
    {
        var localState = localInit();

        foreach (var item in partitionerSource)
        {
            localState = body(item, localState);
        }

        localFinally(localState);
    };

    var tasks = new Task[degreeOfParallelism - 1];

    for (int i = 0; i < degreeOfParallelism - 1; i++)
    {
        tasks[i] = Task.Run(taskAction);
    }

    taskAction();

    Task.WaitAll(tasks);
}

关于c# - 如何防止 Parallel.ForEach 循环在运行时更改任务数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35698775/

相关文章:

c# - 为多个接口(interface)返回相同的实例

c# - 检查列表包含多少相同的项目

java - 如何为我的自制桌面应用程序实现线程

c - PThread 互斥体未按预期工作

c# - Parallel.ForEach 和无法从关闭的 TextReader 异常中读取

c# - SelectiveScrollingGrid 是否只卡住第一列的列?

c# - 在 WinRT 中暂停应用程序后恢复 CaptureElement

c# - pthread_cond_t 的 EventWaitHandle 行为

c# - 基于需要在执行时间内接收更多项目的列表执行 Parallel.ForEach

c# - TPL 并行度启发式