c# - 如何强制结束 Parallel.Invoke 循环?

标签 c# parallel-processing .net-7.0 cancellation parallel.invoke

Compute 函数内部有大量、持久的计算(数十分钟)。我无法编辑此函数,我无权访问源代码。

List<Action> parallelActions = new();
for (int i = 0; i < 1000; i++)
{
    parallelActions.Add(() => Compute(i));
}

Parallel.Invoke(parallelActions.ToArray());

如果循环执行时间超过 maxTime 秒,我想停止循环执行。您知道如何通过立即停止执行来结束并行循环吗?

我尝试添加CancellationToken:

List<Action> parallelActions = new();
for (int i = 0; i < 1000; i++)
{
    parallelActions.Add(() => Compute(i));
}

using CancellationTokenSource cts = new(new TimeSpan(0, 0, maxTime));
ParallelOptions parallelOptions = new() { CancellationToken = cts.Token };

Parallel.Invoke(parallelOptions, parallelActions.ToArray());

问题是,即使我添加了 CancellationToken,已经启动的 Compute 功能也不会停止,所以我必须等待很长时间才能完成。如何更改?

更多信息:我的目标是 .NET 7 平台。每个单独的Compute调用平均需要几十分钟才能完成。

最佳答案

recommended way以非合作方式强制中止工作的方法是在单独的可执行文件上运行工作并通过终止进程来中止工作。这是保护主可执行文件的状态免遭损坏的唯一方法。即使您的目标是支持 Thread.Abort 的 .NET 平台API 与 .NET Framework 一样,使用它会危及应用程序的稳定性及其行为的正确性。

.NET 7 引入了 ControlledExecution.Run方法,该方法执行受控的 Thread.Abort,而不实际终止当前线程。终止会自动取消,并显示 Thread.ResetAbort 。使用 ControlledExecution.Run 方法生成 compilation warning :

The ControlledExecution.Run(Action, CancellationToken) method might corrupt the process, and should not be used in production code.

虽然Thread.Abort是“受控的”,但它可能像原始Thread.Abort一样容易地破坏应用程序的状态。在执行单个 CPU 指令时,线程会在任意时刻中止。不是方法,而是CPU指令。大多数 .NET API 都编译为多个 CPU 指令,并且未针对部分执行其指令集后的恢复进行强化。

假设您了解风险,以下是使用它的方法:

using CancellationTokenSource cts = new();

ParallelOptions options = new()
{
    CancellationToken = cts.Token,
    MaxDegreeOfParallelism = 20 // Configurable
};

ThreadPool.GetMinThreads(out _, out int cpt);
ThreadPool.SetMinThreads(options.MaxDegreeOfParallelism, cpt);

cts.CancelAfter(TimeSpan.FromMinutes(120));

Parallel.For(0, 1000, options, index =>
{
    ControlledExecution.Run(() =>
    {
        Compute(index);
    }, cts.Token);
});

在上面的例子中 ThreadPool.SetMinThreads配置 ThreadPool,以便它立即创建 Parallel.For 所需的所有线程,以便从一开始就达到所需的并行度。如果省略它,ThreadPool 可能需要一些时间才能创建所需数量的线程。

另一种想法是绕过ThreadPool并使用专用线程。您可以找到here自定义 TaskScheduler,为每个任务创建专用后台线程。

关于c# - 如何强制结束 Parallel.Invoke 循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77071891/

相关文章:

multithreading - 如何在self中使用方法使用函数进行线程化?

c# - 书籍推荐 - C# .NET 4.0 的并行编程

azure - 找不到部署 ASP.NET Core 7 Web API somepage.azurewebsites.net 页面

c# - 删除相关实体时出现“集合已修改”错误

c# - 从 C# 中的 dll 加载特定类

c# - 有没有办法在派生类中使用同名但类型不同的属性?

c# - 删除不可打印的字符 C# 多语言

c++ - 按照创建顺序读取/写入 HDF5 文件

c# - 为什么序列化结构是错误的?