一、多线程编程的基本概念(扩展)

1.1 进程 vs 线程 vs 纤程

现实案例对比表(工业/服务器开发常用视角)

维度 进程 线程 纤程 (Fiber) 典型工业/服务器使用场景
资源隔离 最高(独立地址空间) 中等(共享进程地址空间) 最低(完全用户态控制) 微服务隔离 → 进程;高并发业务 → 线程
创建/销毁开销 高(几百 ms ~ 秒级) 中等(几十 ~ 几百 μs) 极低(几 μs) 线程池 vs 纤程在高吞吐场景的取舍
调度主体 操作系统内核 操作系统内核 用户代码手动切换 99% 场景用内核线程,极少数高性能场景用纤程
.NET 默认支持 原生支持 原生支持(Thread / ThreadPool) 不直接支持(需 P/Invoke) .NET 主要用 ThreadPool + Task
上下文切换成本 最高 中等 最低 纤程在某些游戏服务器、数据库中有应用

2025–2026 趋势

  • 大部分 .NET 工业项目仍以 ThreadPool + Task / async await 为主
  • 高并发低延迟场景(如游戏服务器、实时交易)开始少量使用 纤程(通过第三方库如 Fibers.NET 或自己 P/Invoke)
  • 云原生时代,进程隔离(容器/K8s)比线程隔离更受欢迎
1.2 抢占式 vs 非抢占式调度(现代 .NET 补充)

现代 .NET(.NET 6+)线程池行为(2026 年实测):

  • 默认使用抢占式 + 动态线程注入策略
  • 当任务队列积压时,会以指数退避方式增加线程(最多到 Environment.ProcessorCount * 环境因子)
  • .NET 8/9 引入了 ThreadPool.WorkerThreadMin / Max 可配置参数
  • 高负载下会主动“杀死”空闲线程,防止内存浪费

代码示例:查看和调整线程池参数(.NET 8+)

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // 查看当前线程池设置
        ThreadPool.GetMaxThreads(out int maxWorker, out int maxIO);
        ThreadPool.GetMinThreads(out int minWorker, out int minIO);
        ThreadPool.GetAvailableThreads(out int availWorker, out int availIO);

        Console.WriteLine($"最大工作者线程: {maxWorker}, 最大IO线程: {maxIO}");
        Console.WriteLine($"最小工作者线程: {minWorker}, 最小IO线程: {minIO}");
        Console.WriteLine($"可用工作者线程: {availWorker}, 可用IO线程: {availIO}");

        // 工业场景:高并发时适当调大最小线程数(避免频繁创建线程)
        ThreadPool.SetMinThreads(50, 50);  // 根据 CPU 核心数 × 4~8 调整

        // 模拟高并发任务
        for (int i = 0; i < 200; i++)
        {
            ThreadPool.QueueUserWorkItem(_ =>
            {
                Console.WriteLine($"Task {i} running on thread {Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(1000);
            });
        }

        Console.ReadLine();
    }
}

误区提醒

  • 不要盲目把 MinThreads 设得过高(会导致 CPU 争抢、上下文切换暴增)
  • 不要在 ASP.NET Core 中手动调线程池(Kestrel 已优化)

二、.NET 中的多线程编程(更多案例 & 现代写法)

2.1 Thread / ThreadPool 经典用法(补充现代替代)

经典 Thread 用法(不推荐新项目)

// 旧式写法(容易资源泄漏)
var t = new Thread(() =>
{
    try
    {
        // 业务逻辑
    }
    catch (Exception ex)
    {
        Console.WriteLine("未捕获异常: " + ex);
    }
});
t.IsBackground = true;  // 必须设为后台线程
t.Start();

现代推荐:Task.Run + async/await

// 推荐写法(自动捕获异常、支持取消、资源管理更好)
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

try
{
    await Task.Run(async () =>
    {
        while (!cts.Token.IsCancellationRequested)
        {
            Console.WriteLine($"Task running... Thread: {Thread.CurrentThread.ManagedThreadId}");
            await Task.Delay(1000, cts.Token);
        }
    }, cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("任务被取消");
}
catch (Exception ex)
{
    Console.WriteLine("任务异常: " + ex);
}
2.2 ThreadLocal(现代线程本地存储首选)

经典 ThreadStatic 缺点

  • 只支持静态字段
  • 值类型初始化问题(每个线程都会重新执行静态构造)

现代推荐:ThreadLocal

// 线程独享的 DbContext(EF Core 经典用法)
public static class DbContextFactory
{
    private static readonly ThreadLocal<MyDbContext> _context = new(() => 
    {
        var ctx = new MyDbContext();
        Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 创建了 DbContext");
        return ctx;
    });

    public static MyDbContext Current => _context.Value;
}

// 使用
Task.Run(() =>
{
    using var ctx = DbContextFactory.Current;
    // 同一个线程内复用同一个 context
});

ThreadLocal vs ThreadStatic 对比(2026 年推荐)

特性 ThreadStatic ThreadLocal 推荐场景
支持实例字段 ThreadLocal 胜
值类型初始化 每个线程重复构造 延迟构造(Lazy) ThreadLocal 胜
资源释放 手动处理 支持 IDisposable 自动释放 ThreadLocal 胜
性能 略高 略低(但差距微小) 几乎无差别
现代项目使用率 极低 极高 强烈推荐 ThreadLocal
2.3 Task + async/await 高频工业用法(补充完整案例)

案例:批量并行处理订单 + 限流 + 进度报告

public async Task ProcessOrdersAsync(List<Order> orders, IProgress<int> progress)
{
    const int MAX_CONCURRENCY = 10; // 限制最大并发
    using var semaphore = new SemaphoreSlim(MAX_CONCURRENCY);

    var tasks = orders.Select(async order =>
    {
        await semaphore.WaitAsync();
        try
        {
            // 模拟业务处理
            await Task.Delay(500);
            Console.WriteLine($"订单 {order.Id} 处理完成,线程 {Thread.CurrentThread.ManagedThreadId}");

            // 报告进度
            progress?.Report(Interlocked.Increment(ref processedCount));
        }
        finally
        {
            semaphore.Release();
        }
    }).ToList();

    await Task.WhenAll(tasks);
}

前端调用示例(WinForms / MAUI)

private async void btnProcess_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    var progress = new Progress<int>(value => 
    {
        progressBar.Value = value;
        lblStatus.Text = $"已处理: {value}/{orders.Count}";
    });

    await ProcessOrdersAsync(orders, progress);
    MessageBox.Show("全部订单处理完成!");
}
2.4 线程同步高级用法(更多工业案例)

案例1:多线程安全队列(生产者-消费者模型)

public class ThreadSafeQueue<T>
{
    private readonly ConcurrentQueue<T> queue = new();
    private readonly SemaphoreSlim signal = new(0);

    public void Enqueue(T item)
    {
        queue.Enqueue(item);
        signal.Release();
    }

    public async Task<T> DequeueAsync(CancellationToken ct = default)
    {
        await signal.WaitAsync(ct);
        queue.TryDequeue(out var item);
        return item;
    }
}

案例2:异步限流(工业接口调用常用)

public class RateLimiter
{
    private readonly SemaphoreSlim semaphore;
    private readonly TimeSpan resetInterval;
    private int count = 0;
    private DateTime lastReset = DateTime.UtcNow;

    public RateLimiter(int maxRequests, TimeSpan perTime)
    {
        semaphore = new SemaphoreSlim(maxRequests, maxRequests);
        resetInterval = perTime;
    }

    public async Task WaitAsync()
    {
        // 重置计数器
        if (DateTime.UtcNow - lastReset >= resetInterval)
        {
            count = 0;
            lastReset = DateTime.UtcNow;
        }

        await semaphore.WaitAsync();

        // 原子递增
        Interlocked.Increment(ref count);
        if (count > semaphore.CurrentCount)
        {
            await Task.Delay(100); // 简单退避
        }
    }

    public void Release() => semaphore.Release();
}

案例3:并行批量处理 + 异常聚合

public async Task ProcessBatchAsync<T>(IEnumerable<T> items, Func<T, Task> action, int maxDegree = 8)
{
    var exceptions = new List<Exception>();
    var semaphore = new SemaphoreSlim(maxDegree);

    var tasks = items.Select(async item =>
    {
        await semaphore.WaitAsync();
        try
        {
            await action(item);
        }
        catch (Exception ex)
        {
            lock (exceptions) exceptions.Add(ex);
        }
        finally
        {
            semaphore.Release();
        }
    });

    await Task.WhenAll(tasks);

    if (exceptions.Any())
        throw new AggregateException("批量处理失败", exceptions);
}

总结:.NET 多线程编程 2026 年黄金法则

  1. 优先使用 Task + async/await,避免直接 new Thread
  2. 线程独享数据:首选 ThreadLocal<T>
  3. 同步:优先 ConcurrentDictionary / ConcurrentQueue,其次 lock,慎用 Mutex / Semaphore
  4. 限流 & 并发控制SemaphoreSlim + Interlocked 是工业标配
  5. 异常处理:永远用 try-catch 包裹 Task,避免 unobserved exception 闪退
  6. 性能监控:上线前必须测定 ThreadPool 饱和情况、GC 压力、上下文切换次数

需要我针对某个具体场景(例如:WPF/MAUI 高并发 UI 更新、ASP.NET Core 高吞吐限流、WinForms 实时采集数据处理、工业设备轮询等)给出更详细的完整案例代码吗?直接告诉我,我立刻补充!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐