一、线程与进程概念

进程(Process):

  • 一个正在运行的程序实例(如 MyApp.exe
  • 拥有独立内存空间
  •  资源隔离(一个崩溃不影响其他进程)

线程:

  • 进程内的执行单元
  • 共享进程的内存和资源
  • 一个进程至少有 1 个线程(主线程)

当你启动 WPF 程序(App.xaml → MainWindow),第一个线程就是 UI 线程,当不使用多线程后台有耗时操作会导致ui卡顿,所有 UI 控件(Button、TextBox 等)必须在 UI 线程创建和更新。

二、Thread和Task开线程

2.1.优劣对比

特性 Thread Task
抽象层级 底层(直接操作系统线程) 高层(基于线程池的任务调度)
资源开销 高(每次 new Thread() 都创建新线程) 低(复用线程池线程)
返回值 ❌ 不能直接返回值 ✅ Task<T> 可返回结果
异常处理 难(需手动 try/catch) ✅ 自动捕获,可通过 await 或 task.Exception 获取
取消支持 需手动实现(如 Thread.Abort() 已废弃) ✅ 原生支持 CancellationToken
与 async/await 集成 ❌ 不支持 ✅ 完美支持
适用场景 长时间运行、独占线程(如消息循环) 绝大多数异步场景(I/O、计算、网络)

得出结论:绝大部分情况都使用Task,因此下面的3.1只讨论Task开线程的方法

2.2多线程实现

2.2.1Thread实现开线程

   private  void Button_Click_1(object sender, RoutedEventArgs e)
   {
        Thread要求输入一个函数或者委托
       Thread t = new Thread(() =>
       {
           Thread.Sleep(5000);
           MessageBox.Show("做菜");
           Thread.Sleep(3000);
           MessageBox.Show("吃饭");
       });
       t.Start();
   }

2.2.2Task实现开线程

直接反编译,Task就是一个类,run是类的静态方法,返回值为Task

注意下面两个线程的独立的,也就是并行执行的

     private void Button_Click_2(object sender, RoutedEventArgs e)
     {
//Task
         Task.Run(() => {
             Thread.Sleep(2000);
             MessageBox.Show("做菜");
         });
         Task.Run(() =>
         {
             Thread.Sleep(2000);
             MessageBox.Show("吃饭");
         });
         MessageBox.Show("主线程继续执行");
     }

三、异步与多线程

3.1ui线程

3.1.1概念

UI 线程是wpf的主线程,当你没有显示的开启线程,所有东西都会在这个线程运行;本质上运行一个 无限循环,称为 消息循环(Message Loop),所有 UI 操作最终都变成“消息”进入这个队列,由 UI 线程顺序处理

3.1.2特性

  1. UI 线程是应用程序中专门负责创建、更新和响应用户界面的唯一主线程
  2. 所有 UI 元素(窗口、控件)的更新,创建必须在ui线程中操作

3.2几种常见的异步方法和使用场景

async void异步函数用法

// ✅ 唯一推荐场景:事件处理
private async void Button_Click(object sender, RoutedEventArgs e)
{
    await Task.Delay(1000);
    MessageBox.Show("完成!");
}

async Task异步函数用法

重点:等同于void的同步函数,实际上,返回了一个Task的对象,这个是c#自动生成的

在返回值为Task的异步函数中,不需要去管这个Task类型的返回值,业务代码完全看作是无返回值类型的函数去做就可以,即:等同于void的同步函数

 private async Task MyMethod()
 {
     return; // 提前退出
 }

 private async void Button_Click(object sender, RoutedEventArgs e)
 {
     Task task = MyMethod(); // 获取返回的 Task
     await task; // 等待它

     // 验证 Task 状态
     Debug.WriteLine($"IsCompleted: {task.IsCompleted}"); // True
     Debug.WriteLine($"IsFaulted: {task.IsFaulted}");     // False
     Debug.WriteLine($"Status: {task.Status}");           // RanToCompletion
 }

task.IsCompleted是表示的是 “方法本身是否执行完毕”不是 “方法内部启动的所有任务是否完成”!

task.Status是是否执行

注意:上面代码中使用了Button_Click异步函数去调用MyMethod异步函数,那么必须使用awit,否则不会等待执行完成,将继续执行Button_Click的代码

async Task<T>异步函数用法

有返回值的必须显式返回,这点和同步一样

// ✅ 有返回值的异步方法
public async Task<string> ReadFileAsync()
{
    string content = await File.ReadAllTextAsync("data.txt");
    return content; // ✅ 必须返回 string
}

// 调用方获取返回值
private async void Button_Click(...)
{
    try
    {
        string data = await ReadFileAsync(); // ✅ data 是 string 类型
        textBox.Text = data;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"读取失败: {ex.Message}");
    }
}

3.3Task是什么

定义

Task就是一个类,主要作用:用于和await搭配,并且返回当前的被调用方的状态

常用属性

task.IsCompleted是表示的是 “方法本身是否执行完毕”不是 “方法内部启动的所有任务是否完成”!返回值为空的函数执行完毕的标准是:return;和函数的方法体执行完毕。

特性

在调用返回值为Task的方法时,Task是立即返回的,下面代码做一个实验

 public async Task click_Execute()
 {
    await Task.Run(() => { Thread.Sleep(3000); });
 }
 private async void Button_Click(object sender, RoutedEventArgs e)
 {
     Task task=click_Execute();
    // await task;
     Debug.WriteLine(task.IsCompleted);
     MessageBox.Show("UI线程回到这个函数");
 }

当我不在调用函数不使用await时候,Button_Click函数不等待click_Execute执行完毕,但是此时Task对象以及返回。输出参数如下:

3.4async和await

await是异步的灵魂,async只是异步函数标记

await作用

修改函数的执行顺序,当ui线程发现await时,会跳出这个函数等待await函数后面执行完成再回来继续执行这个函数

await的使用

后面只能接返回Task的方法,例如:Task.Run和另外一个返回Task的异步方法

四、同步异步和多线程的结合使用实例

同步异步和多线程组合实例

点击事件1:同步单线程,ui界面阻塞

点击事件2:异步单线程,ui界面阻塞

点击事件2:多线程同步方法,当单开的线程执行时候,多线程的部分交给单独的线程执行,函数直接继续执行语句直到函数执行完毕,ui界面不阻塞

点击事件3:多线程异步,当在异步方法中使用await时这个关键字,函数先在await处将多线程的部分交给子线程,ui线程回来处理其他程序,当子线程执行完成,继续执行本函数,ui界面不阻塞。(这个方式才是多线程和异步的正确使用方式,单独使用多线程,会破坏逻辑顺序根本没有单独使用的必要)

//同步单线程
private void Button_Click_1(object sender, RoutedEventArgs e)
 {
     Thread.Sleep(3000);
     MessageBox.Show("Button clicked!");
 }
//异步单线程
 private async void Button_Click_2(object sender, RoutedEventArgs e)
 {

     Thread.Sleep(3000);
     MessageBox.Show("Button clicked!");
 }
//同步多线程
 private void Button_Click_3(object sender, RoutedEventArgs e)
 {
     Task.Run(() => {
         Thread.Sleep(3000);
         MessageBox.Show("线程执行完成");
     });

     MessageBox.Show("Button clicked!");
 }异步多线程
 private  async void Button_Click_4(object sender, RoutedEventArgs e)
 {
    await Task.Run(() => {
         Thread.Sleep(3000);
        MessageBox.Show("线程执行完成");
    } );
  
     MessageBox.Show("Button clicked!");
 }

结论

  1. ui界面不卡顿,UI 不卡顿的关键是耗时操作不在 UI 线程执行
  2. 异步只是配合多线程执行函数顺序的工具,即:使用await来等待子线程执行完成,继续执行函数剩下部分内容
  3. await是异步的灵魂,async只是异步函数标记

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐