Task.Run +Task.WhenAll 与 Paraller之一

在C#中,Task.Run配合Task.WhenAllParallel类都是用于并行处理以提高性能的常见方法,但它们的使用场景和内部机制有所不同。

1. Task.Run + Task.WhenAll

这种方法适用于I/O密集型操作(如网络请求、文件读写等)或CPU密集型操作,但更侧重于异步操作。它通过将多个任务并行启动,然后等待所有任务完成。
使用场景

  • 当操作是异步的(如使用async/await)时,这种方法更自然。
  • 每个任务相对独立,不需要共享资源(如果共享资源,需要注意线程安全)。
  • 任务数量不是特别大(虽然可以创建很多任务,但要注意资源限制)。

2. Parallel类

Parallel类(Parallel.ForParallel.ForEach)主要用于数据并行,适用于CPU密集型操作,它使用多个线程来并行处理数据集合。它是基于任务的,但内部使用线程池,并且是同步的(阻塞当前线程直到所有操作完成)。
使用场景

  • CPU密集型操作,例如大量计算。
  • 处理集合中的每个元素,且每个元素的处理是独立的(或通过线程安全的方式共享状态)。
  • 当操作是同步的,并且你希望利用多核处理器。

在C#中,Task.Run + Task.WhenAllParallel 类都是并行处理的技术,但适用场景和实现方式不同。以下是选择建议和对应的取消操作实现:


一、如何选择?

特性 Task.Run + Task.WhenAll Parallel
适用场景 I/O密集型操作、异步任务、独立任务 CPU密集型操作、数据并行处理
并行控制 手动控制(通过任务列表) 自动分区(通过ParallelOptions配置)
阻塞性 非阻塞(async/await) 阻塞(同步操作)
任务粒度 粗粒度(独立任务) 细粒度(数据集合元素)
异常处理 通过AggregateException捕获所有异常 同左
资源开销 较高(每个任务独立调度) 较低(优化线程池使用)

选择建议

  1. 优先用 Task.WhenAll

    • 处理 I/O密集型 操作(如API调用、文件读写)
    • 需要 异步等待 结果时(避免阻塞线程)
    • 任务逻辑 独立且数量动态变化
  2. 优先用 Parallel

    • 处理 CPU密集型 计算(如图像处理、数值计算)
    • 需要 高效处理数据集合(如数组、列表的并行循环)
    • 需要 限制并发度(通过MaxDegreeOfParallelism

二、取消操作实现

1. Task.Run + Task.WhenAll 的取消

使用 CancellationTokenSource 传递取消令牌。

public async Task RunTasksWithCancellationAsync()
{
    var cts = new CancellationTokenSource();
    
    // 创建任务列表(传入取消令牌)
    var tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
    {
        tasks.Add(Task.Run(() => DoWorkAsync(cts.Token), cts.Token));
    }

    // 外部触发取消(例如超时或用户操作)
    cts.CancelAfter(TimeSpan.FromSeconds(5)); // 5秒后自动取消

    try
    {
        await Task.WhenAll(tasks);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("任务已取消");
    }
}

private async Task DoWorkAsync(CancellationToken token)
{
    while (!token.IsCancellationRequested)
    {
        await Task.Delay(1000, token); // 模拟异步操作
        token.ThrowIfCancellationRequested(); // 检查取消
    }
}

2. Parallel 的取消

通过 ParallelOptions 传递取消令牌。

public void RunParallelWithCancellation()
{
    var cts = new CancellationTokenSource();
    var options = new ParallelOptions
    {
        CancellationToken = cts.Token,
        MaxDegreeOfParallelism = 4 // 限制并发数
    };

    cts.CancelAfter(TimeSpan.FromSeconds(5)); // 5秒后取消

    try
    {
        Parallel.For(0, 100, options, (i, state) =>
        {
            options.CancellationToken.ThrowIfCancellationRequested();
            DoCpuBoundWork(i); // 模拟CPU密集型操作
        });
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("并行循环已取消");
    }
}

private void DoCpuBoundWork(int index)
{
    Thread.Sleep(500); // 模拟CPU工作
}

三、关键注意事项

  1. 资源释放
    • 在取消后及时释放资源(如数据库连接、文件句柄)。
  2. 取消响应
    • 在循环或长时间操作中 定期检查 token.IsCancellationRequested
  3. 异常聚合
    • 两者都会抛出 AggregateException,需遍历 InnerExceptions 处理具体错误。
  4. 异步兼容性
    • Parallel 不支持 async 委托(内部用同步方法),若需异步并行循环,使用 Task.WhenAll

四、总结

  • Task.WhenAll:适合 异步、I/O密集型、异构任务
  • Parallel:适合 同步、CPU密集型、同构数据并行
  • 取消机制:统一通过 CancellationToken 实现,注意在任务内部定期检查取消状态。
posted @ 2025-08-17 18:24  青云Zeo  阅读(62)  评论(0)    收藏  举报