C#(多线程和同步异步)
Task就是一个类,主要作用:用于和await搭配,并且返回当前的被调用方的状态。
一、线程与进程概念
进程(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特性
- UI 线程是应用程序中专门负责创建、更新和响应用户界面的唯一主线程
- 所有 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!");
}
结论
- ui界面不卡顿,UI 不卡顿的关键是耗时操作不在 UI 线程执行
- 异步只是配合多线程执行函数顺序的工具,即:使用await来等待子线程执行完成,继续执行函数剩下部分内容
- await是异步的灵魂,async只是异步函数标记
更多推荐



所有评论(0)