.NET 多线程 C# 多线程 持续更新、完善、进化

在 .NET环境下,多线程编程主要有 Thread ThreadPool Task Parallel BackgroundWorker 等几种,还有一个与多线程相关的:异步编程 async/await , 值得强调的是,异步编程不等于多线程。当然,这几种多线程编程的方式并不是独立开的,在底层的封装有一定的联系。

1. Thread

1.1 基础应用

引用Thread的命名空间

cs 复制代码
using System;
using System.Threading;

直接创建线程进行启动

cs 复制代码
static void Main()
{
    // 创建一个新线程
    Thread thread = new Thread(DoWork);
    thread.Start(); // 启动线程

    // 主线程继续执行
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是主线程打印: " + i);
        Thread.Sleep(500);
    }
    Console.ReadKey();
}

static void DoWork()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是自主创建的线程打印: " + i);
        Thread.Sleep(500);
    }
}

调试结果:

cs 复制代码
这是自主创建的线程打印: 0
这是主线程打印: 0
这是自主创建的线程打印: 1
这是主线程打印: 1
这是主线程打印: 2
这是自主创建的线程打印: 2
这是主线程打印: 3
这是自主创建的线程打印: 3
这是主线程打印: 4
这是自主创建的线程打印: 4
这是主线程打印: 5
这是自主创建的线程打印: 5
这是主线程打印: 6
这是自主创建的线程打印: 6
这是主线程打印: 7
这是自主创建的线程打印: 7
这是主线程打印: 8
这是自主创建的线程打印: 8
这是主线程打印: 9
这是自主创建的线程打印: 9

可以发现,主线程和自己创建的线程各干各的,互不耽搁。

1.2 线程入参

如果我在启动线程时想给他一个参数呢?这样搞:

1. 在委托方法设置参数

cs 复制代码
static void Main()
{
    // 创建一个新线程
    Thread thread = new Thread(DoWork);
    thread.Start("子线程启动啦"); // 启动线程

    // 主线程继续执行
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是主线程打印: " + i);
        Thread.Sleep(500);
    }
    Console.ReadKey();
}

static void DoWork(object obj)
{
    // 将传入的 object 类型参数转换为实际类型
    string message = (string)obj;
    Console.WriteLine($"Received message in Main thread: {message}");

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是自主创建的线程打印: " + i);
        Thread.Sleep(500);
    }
}

调试结果:

cs 复制代码
这是主线程打印: 0
Received message in Main thread: 子线程启动啦
这是自主创建的线程打印: 0
这是自主创建的线程打印: 1
这是主线程打印: 1
这是主线程打印: 2
这是自主创建的线程打印: 2
这是主线程打印: 3
这是自主创建的线程打印: 3
这是主线程打印: 4
这是自主创建的线程打印: 4
这是主线程打印: 5
这是自主创建的线程打印: 5
这是主线程打印: 6
这是自主创建的线程打印: 6
这是主线程打印: 7
这是自主创建的线程打印: 7
这是自主创建的线程打印: 8
这是主线程打印: 8
这是主线程打印: 9
这是自主创建的线程打印: 9

2. 使用Lambda表达式

cs 复制代码
static void Main()
{
    // 创建一个新线程
    // 使用 Lambda 表达式创建线程并传递参数
    Thread thread = new Thread(() =>
    {
        DoWork("子线程又又又启动啦");
    });

    // 启动线程
    thread.Start();

    // 主线程继续执行
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是主线程打印: " + i);
        Thread.Sleep(500);
    }
    Console.ReadKey();
}

static void DoWork(object obj)
{
    // 将传入的 object 类型参数转换为实际类型
    string message = (string)obj;
    Console.WriteLine($"Received message in Main thread: {message}");

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是自主创建的线程打印: " + i);
        Thread.Sleep(500);
    }
}

调试运行结果:

cs 复制代码
这是主线程打印: 0
Received message in Main thread: 子线程又又又启动啦
这是自主创建的线程打印: 0
这是主线程打印: 1
这是自主创建的线程打印: 1
这是主线程打印: 2
这是自主创建的线程打印: 2
这是主线程打印: 3
这是自主创建的线程打印: 3
这是主线程打印: 4
这是自主创建的线程打印: 4
这是主线程打印: 5
这是自主创建的线程打印: 5
这是主线程打印: 6
这是自主创建的线程打印: 6
这是主线程打印: 7
这是自主创建的线程打印: 7
这是主线程打印: 8
这是自主创建的线程打印: 8
这是主线程打印: 9
这是自主创建的线程打印: 9

2. ThreadPool

System.Threading.ThreadPool 是一个线程池,用于管理和复用线程,适合处理大量短生命周期的任务。其实这个是对Thread的封装,从某种程度上讲节省了程序运行的开销。

cs 复制代码
static void Main()
{
    // 将任务加入线程池
    ThreadPool.QueueUserWorkItem(DoWork);

    // 主线程继续执行
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是主线程打印: " + i);
        Thread.Sleep(500);
    }
    Console.ReadKey();
}

static void DoWork(object obj)
{
    // 将传入的 object 类型参数转换为实际类型
    string message = (string)obj;
    Console.WriteLine($"Received message in Main thread: {message}");

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("这是自主创建的线程打印: " + i);
        Thread.Sleep(500);
    }
}

运行调试结果

cs 复制代码
这是主线程打印: 0
Received message in Main thread:
这是自主创建的线程打印: 0
这是自主创建的线程打印: 1
这是主线程打印: 1
这是主线程打印: 2
这是自主创建的线程打印: 2
这是主线程打印: 3
这是自主创建的线程打印: 3
这是主线程打印: 4
这是自主创建的线程打印: 4
这是主线程打印: 5
这是自主创建的线程打印: 5

3. Task

System.Threading.Tasks.Task 是基于任务的异步编程模型(TAP),是对ThreadPool的进一步封装,提供了更高级的功能。感觉套娃是微软的强项哈哈。

cs 复制代码
static void Main()
{
    // 创建一个任务并启动
    Task task = Task.Run(() => DoWork());

    // 主线程继续执行
    for (int i = 0; i < 4; i++)
    {
        Console.WriteLine("这是主线程打印: " + i);
        Task.Delay(500).Wait();
    }

    task.Wait(); // 等待任务完成
    Console.ReadKey();
}

static void DoWork()
{
    
    for (int i = 0; i < 7; i++)
    {
        Console.WriteLine("这是自主创建的线程打印: " + i);
        Thread.Sleep(500);
    }
}

调试执行:

cs 复制代码
这是主线程打印: 0
这是自主创建的线程打印: 0
这是自主创建的线程打印: 1
这是主线程打印: 1
这是自主创建的线程打印: 2
这是主线程打印: 2
这是主线程打印: 3
这是自主创建的线程打印: 3
这是自主创建的线程打印: 4
这是自主创建的线程打印: 5
这是自主创建的线程打印: 6

从综合性能和实战经验来看,Task应该是应用最多的,主要体现在以下几个特点

  • 支持任务并行和异步操作。

  • 可以获取任务的执行结果(通过Task<TResult>)。

  • 支持任务取消、延续和异常处理。

4. Parallel

System.Threading.Tasks.Parallel 提供了简单的并行循环和并行任务执行。

cs 复制代码
 // 并行执行循环
        Parallel.For(0, 5, i =>
        {
            Console.WriteLine("Parallel thread: " + i);
            Task.Delay(500).Wait();
        });

调试运行:

cs 复制代码
Parallel thread: 3
Parallel thread: 0
Parallel thread: 4
Parallel thread: 1
Parallel thread: 2
  • 简化并行循环的编写。

  • 自动管理线程池中的线程。

这种模式适用于任务并行分发的场景,简单,高效。

5. BackgroundWorker

System.ComponentModel.BackgroundWorker 是一个用于在后台执行任务的组件,适合在UI应用程序中使用。

代码示例:

cs 复制代码
static void Main()
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += DoWork;
    worker.RunWorkerCompleted += WorkCompleted;

    worker.RunWorkerAsync(); // 启动后台任务

    Console.WriteLine("Main thread continues...");

    Console.ReadKey();
}
static void DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("工作线程: " + i);
        System.Threading.Thread.Sleep(500);
    }
}

static void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    Console.WriteLine("任务完成.");
}

运行结果:

cs 复制代码
Main thread continues...
工作线程: 0
工作线程: 1
工作线程: 2
工作线程: 3
工作线程: 4
任务完成.

6. async/await

**async/await不是多线程。**只是和多线程相关,具体来说是和Task直接相关。

代码示例1:

cs 复制代码
static async Task Main()
{
    Console.WriteLine("主线程启动.");

    // 异步调用
    await DoWorkAsync();

    Console.WriteLine("主线程完成.");

    Console.ReadKey();
}
static async Task DoWorkAsync()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("异步方法执行: " + i);
        await Task.Delay(500);
    }
}

运行结果:

cs 复制代码
主线程启动.
异步方法执行: 0
异步方法执行: 1
异步方法执行: 2
异步方法执行: 3
异步方法执行: 4
主线程完成.

或者这样写:

cs 复制代码
static async Task DoWorkAsync()
{
    for (int i = 0; i < 5; i++)
    {
        await Task.Run(() =>
        {

            Console.WriteLine("异步方法执行: " + i);
            Task.Delay(500);

        });
    }
}

关于异步编程,在杨中科的书里学了一点:第2章 .NETCore异步编程 C#异步编程 关键字async/await用法

DeepSeek总结:

种类 适用场景 优点 缺点
Thread 需要精细控制线程的场景 直接控制线程生命周期 手动管理线程,开销较大
ThreadPool 大量短生命周期任务 线程复用,减少开销 不适合长时间任务
Task 异步编程和任务并行 功能强大,支持任务结果和异常处理 需要理解异步编程模型
Parallel 简单的并行循环 简化并行编程 不适合复杂任务
async/await 异步编程,避免阻塞主线程 代码简洁,易于理解 需要理解异步编程模型
BackgroundWorker UI应用程序中的后台任务 支持进度报告和取消操作 功能相对简单

7. 线程间的通讯

相关推荐
黄明基2 分钟前
设计模式学习:2、状态模式实现订单状态流转
设计模式·.net·dotnet core
单单单单点2 小时前
C#+Halcon 垂直拼接/裁切避免频繁开辟内存
图像处理·c#
Nita.2 小时前
设计模式|策略模式 Strategy Pattern 详解
设计模式·c#·策略模式
WangMing_X3 小时前
C#实现语音合成播报器——基于System.Speech的语音交互方案,在windows上实现语音播报指定文本
c#·语音识别·语音播报
金士顿5 小时前
.net framework 3.5安装
.net
“抚琴”的人5 小时前
C#—csv文件格式操作实例【在winform表格中操作csv】
开发语言·c#
胖头鱼不吃鱼-5 小时前
AI与.NET技术实操系列
人工智能·.net
我不是程序猿儿6 小时前
【C#】检查已有窗口,防止重复打开
开发语言·c#
ww,pw6 小时前
.Net 6 上传文件接口 文件大小报错整体配置
c#