C# Thread与Task的区别
Thread与Task的区别
一.进阶认识
- 进程与线程
- 同步与异步
- 阻塞与非阻塞
1.进程(process)
当一个程序开始的时候,它是一个进程, 进程包括运行中的程序和程序所用到的内存和系统资源
注意: 线程是由多个线程所组成的
2.线程(thread)
单线程就相当于:一个人正在做饭,做完饭去抽烟,然后回来吃饭
多线程就相当于: 一个人一边在做着饭的同时, 也抽着烟,同时进行
3.同步(sync):
发出一个功能调用时, 在没有得到结果之前, 该调用就不返回
更详细的就是: 同步就是你在做一件事情的时候,这件事没做完你做不了别的
4.异步(async)
与同步相对, 调用在发出之后, 这个调用就直接返回了, 所以没有返回结果. 当这个结果调用完成后, 一般通过状态, 通知和回调来通知调用者. 对于异步调用, 调用的返回不受调用者控制
例如: 你一边打电话一般抽着烟.互不影响
5.阻塞(block)
阻塞调用是指调用结果返回之前, 当前线程会被挂起,就不继续执行后续操作
可以理解为:上一件事没做完,就不会做下一件事
6.非阻塞(non-block)
非阻塞调用指在不能立刻得到结果之前, 该调用不会阻塞当前线程
同步阻塞 | 你跟你朋友聊天,但你朋友上厕所了,你没有离开就一直在那边等,等到它出来 |
---|---|
同步非阻塞 | 你跟你朋友聊天,但是它上厕所期间,你没有离开,你一直等他的同时,抽了根烟(运用中很难遇到) |
异步阻塞 | 你给朋友打电话,但你朋友那边在忙说一会打给你,这期间你什么都没有做,等到回复电话之后,再继续下一步(没有意义) |
异步非阻塞 | 你朋友打电话,你朋友忙说等等打给你, 这期间你可以做任何事 |
二.线程与同步异步的关系
单线程可以异步操作么? 当时可以,比如延迟方法, 就是典型的异步
但是单线程异步和多线程异步还是有区别的, 因为单线程异步操作在通知者调用之前,是没有跑任何代码的, 因为没有任何线程提供给它.
例如:
- 单线程同步: 甲方线搬了一块砖
- 多线程同步: 甲方先搬了一块砖,乙方又搬了一块砖
- 多线程异步: 甲乙同时搬了一块砖
三.Thread与Task区别
Thread类 .NET中最基础的多线程编程方式, 它直接接代表了操作系统. 使用Thread可以直接接控制线程的创建, 启动, 暂停, 终止等操作.
Task是**.NET 4.0**引入的一个高级抽象, 用于表示异步操作. Task基于线程池技术, 他可以更高效的管理和调度线程资源
1.Task 是基于 Thread 的,是比较高层级的封装, Task 最终还是需要 Thread来执行
Task特点:
-
Task是对线程的高级抽象, 隐藏了线程管理的细节, 使用起来更加方便
-
基于线程池技术,线程池可以复用线程, 减少线程创建和销毁的开销
-
Task与async 和await 关键字结合使用, 使得异步编程更加简介和直观
2.Task 默认使用后台线程执行, Thread 默认使用前台线程
Thread特点
- 可以对线程进行非常细粒度的控制,如设置线程的优先级、挂起和恢复线程等。
- 创建和销毁线程的开销相对较大,因为它直接与操作系统的线程对应。
- 需要手动处理线程之间的同步问题,如使用
lock
语句来避免竞态条件。
static void Main(string[] args)
{
Thread thread=new Thread(()=>{Thread.Sleep(3000);});
thread.Start();
}
上一个代码,主程序在3秒后结束,下面的代码会瞬间结束
static void Main(string[] args)
{
Task<int>task=new Task<int>(()=>
{
Thread.Sleep(3000);
return 1;
}
task.start();
}
Thread 示例代码
using System;
using System.Threading;
class Program
{
static void ThreadMethod()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Thread: {i}");
Thread.Sleep(100);
}
}
static void Main()
{
// 创建一个新的线程
Thread newThread = new Thread(ThreadMethod);
// 启动线程
newThread.Start();
// 主线程继续执行
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"Main: {i}");
Thread.Sleep(150);
}
// 等待新线程执行完毕
newThread.Join();//用来阻塞线程,直到线程执行完毕
Console.WriteLine("Main thread finished.");
}
}
Task示例代码
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task TaskMethod()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Task: {i}");
await Task.Delay(100);
}
}
static async Task Main()
{
// 创建并启动一个任务
Task task = TaskMethod();
// 主线程继续执行
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"Main: {i}");
await Task.Delay(150);
}
// 等待任务执行完毕
await task;
Console.WriteLine("Main thread finished.");
}
}
对比总结
Thread | Task |
---|---|
适用于需要对线程进行底层控制,如设置线程优先级、手动管理线程生命周期等场景。 | 适用于大多数异步编程场景,尤其是需要处理大量并发任务时,使用 Task 可以更高效地利用系统资源。 |
由于线程创建和销毁的开销较大,频繁创建和销毁线程会导致性能下降。 | 基于线程池技术,减少了线程创建和销毁的开销,性能相对较高。 |
需要手动处理线程同步和资源管理,编程复杂度较高 | 与 async 和 await 结合使用,编程模型更加简洁,降低了异步编程的复杂度。 |
可以更高效地利用系统资源。 |
| 由于线程创建和销毁的开销较大,频繁创建和销毁线程会导致性能下降。 | 基于线程池技术,减少了线程创建和销毁的开销,性能相对较高。 |
| 需要手动处理线程同步和资源管理,编程复杂度较高 | 与 async
和 await
结合使用,编程模型更加简洁,降低了异步编程的复杂度。 |