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

发布于:2025-03-06 ⋅ 阅读:(16) ⋅ 点赞:(0)

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

1. Thread 

1.1 基础应用

引用Thread的命名空间

using System;
using System.Threading;

直接创建线程进行启动

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);
    }
}

调试结果:

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

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

1.2 线程入参

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

1. 在委托方法设置参数

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);
    }
}

调试结果:

这是主线程打印: 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表达式

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);
    }
}

调试运行结果:

这是主线程打印: 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的封装,从某种程度上讲节省了程序运行的开销。

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);
    }
}

运行调试结果

这是主线程打印: 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的进一步封装,提供了更高级的功能。感觉套娃是微软的强项哈哈。

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);
    }
}

调试执行:

这是主线程打印: 0
这是自主创建的线程打印: 0
这是自主创建的线程打印: 1
这是主线程打印: 1
这是自主创建的线程打印: 2
这是主线程打印: 2
这是主线程打印: 3
这是自主创建的线程打印: 3
这是自主创建的线程打印: 4
这是自主创建的线程打印: 5
这是自主创建的线程打印: 6

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

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

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

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

4. Parallel 

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

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

调试运行:

Parallel thread: 3
Parallel thread: 0
Parallel thread: 4
Parallel thread: 1
Parallel thread: 2
  • 简化并行循环的编写。

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

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

5. BackgroundWorker

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

代码示例:

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("任务完成.");
}

运行结果:

Main thread continues...
工作线程: 0
工作线程: 1
工作线程: 2
工作线程: 3
工作线程: 4
任务完成.

6. async/await

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

代码示例1:

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);
    }
}

运行结果:

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

或者这样写:

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. 线程间的通讯


网站公告

今日签到

点亮在社区的每一天
去签到