it编程 > 编程语言 > Asp.net

C#中使用Parallel.ForEachAsync异步操作多线程的方法

41人参与 2026-07-24 Asp.net

1. 核心概念

parallel.foreachasync 是在 .net 6 中引入的方法。
它的核心作用是:对一组数据执行异步操作,并且同时控制并发数量(也就是同时跑多少个任务)
它完美解决了两个痛点:

  1. 传统的 parallel.foreach 不支持 async/await(它是为阻塞式 cpu 任务设计的)。
  2. 直接用 task.whenall(tasks) 会一次性把所有任务扔出去,如果数据有 10 万条,会瞬间耗尽内存或网络端口。

2. 基本语法与关键参数

public static task foreachasync<tsource>(
    this ienumerable<tsource> source,
    paralleloptions paralleloptions,
    func<tsource, cancellationtoken, valuetask> body
)

关键参数说明

3. 经典示例

示例 1:批量并发下载网页/api请求(最常见场景)

假设我们要下载 100 个网页,如果串行可能需要 1 分钟,但如果同时开 10 个并发,只需 6 秒。

using system.net.http;
list<string> urls = geturls(); // 假设有 100 个 url
using httpclient client = new httpclient();
// 配置并行选项:最大并发 10
var options = new paralleloptions { maxdegreeofparallelism = 10 };
console.writeline("开始并发下载...");
await parallel.foreachasync(urls, options, async (url, ct) =>
{
    try
    {
        // 传入 ct,如果外部取消,当前请求会立即中止
        string content = await client.getstringasync(url, ct);
        console.writeline($"成功下载 {url},长度: {content.length}");
    }
    catch (operationcanceledexception)
    {
        // 被取消
    }
    catch (exception ex)
    {
        console.writeline($"下载失败 {url}: {ex.message}");
    }
});
console.writeline("全部下载完成。");

示例二:收集结果

foreachasync 本身没有返回值(返回 task),如果需要收集处理结果,必须使用线程安全集合(如 concurrentbag 或 concurrentqueue)。**

using system.collections.concurrent;
using system.diagnostics;

public static async task example_withresultcollection()
{
    var inputs = enumerable.range(1, 10);
    var results = new concurrentbag<int>(); // 线程安全集合

    await parallel.foreachasync(inputs, async i =>
    {
        await task.delay(100); // 模拟耗时操作
        int result = i * i;    // 计算逻辑
        results.add(result);   // 并发添加结果(线程安全)
        
        console.writeline($"{datetime.now:mm:ss:fff} - 处理 {i}");
    });

    console.writeline($"结果数量: {results.count}");
}

示例三:配合取消令牌

在用户关闭窗口或点击“停止”按钮时优雅地停止处理。

public static async task example_withcancellation(cancellationtoken cts)
{
    var range = enumerable.range(0, 1000);
    
    try
    {
        await parallel.foreachasync(range, 
            new paralleloptions { cancellationtoken = cts, maxdegreeofparallelism = 10 }, 
            async (i, ct) =>
            {
                // 如果外部取消,这里会抛出 operationcanceledexception
                await task.delay(100, ct); 
                console.writeline($"处理中: {i}");
            });
    }
    catch (operationcanceledexception)
    {
        console.writeline("任务被用户取消。");
    }
}

4. 核心优势对比表

特性foreach + awaittask.whenallparallel.foreachasync
执行方式串行(一个接一个)全量并发(同时启动所有)可控并发(池化执行)
内存/资源占用极低极高(容易内存溢出/端口耗尽)适中(按需启动)
总耗时最长最短(如果不崩溃的话)较短(与并发数成正比)
支持取消支持支持完美支持

5. 注意事项与避坑指南

必须限制并发数
不设置 maxdegreeofparallelism 时,默认可能会使用处理器数量作为并发度。对于网络请求,建议显式设置为 520 之间(根据目标服务器的承受能力和你的网络带宽决定),防止把对方服务器打挂,或触发对方防爬机制被封 ip。

线程安全问题(最容易踩的坑)
在并发环境下,parallel.foreachasync 的内部相当于多线程同时运行。如果你在里面修改了 ui 绑定的集合(如 observablecollection)或普通的计数字典,极大概率会报错或数据错乱

异常处理

以上就是c#中使用parallel.foreachasync异步操作多线程的方法的详细内容,更多关于c# parallel.foreachasync多线程异步操作的资料请关注代码网其它相关文章!

(0)

您想发表意见!!点此发布评论

推荐阅读

C#代码实现将PDF文件转换为Markdown格式

07-26

C#代码实现在Excel中编辑或删除批注

07-23

C#/Unity清理非托管资源的代码详解

07-27

C#与MySQL数据库交互全攻略

07-28

C#中处理字符串的三种方式

07-28

C#代码轻松实现Excel和Markdown互转

07-21

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论