目录
- 一、基本概念
- [二、Task 的基础实现](#二、Task 的基础实现)
-
- 1、最简单基础的表达
- 2、带返回参数的表达
- 3、无返回值等待任务结束
- 4、停止业务
- 4、其他功能和状态
-
- [(1) 等待任务](#(1) 等待任务)
- (2)获取状态
- 三、总结
一、基本概念
- 在 C# 中,Task(任务) 属于 System.Threading.Tasks 命名空间中。自 .NET 4.0 引入以来,已成为 C# 异步和并行编程的核心基础功能。
- Task 就是一个 "待执行的工作单元",由 线程池(ThreadPool) 管理,所以实质上也是线程处理,与主线程不冲突。因为不用手动创建 / 销毁线程,更轻量、更高效、更易管理。
- 任务的两种核心用途,即:1. 后台执行任务(多线程)不阻塞主线程,让代码在后台运行;2. 异步操作(I/O 操作)文件读写、网络请求、数据库查询等,不占用 CPU主线程。
二、Task 的基础实现
1、最简单基础的表达
- Task.Run(() =>{...});是最Task任务最精简表达,参看下面的代码实例。
- 注意System.Threading.Task记得加上,否则每次在调用时代码时写全。
- => 后的大括号内可以直接执行任务也可以封装函数来运行。
c
using System;
using System.Threading.Tasks;
void MyTask()
{
Console.WriteLine("任务开始");
// 启动一个后台任务
Task.Run(() =>
{
Console.WriteLine("任务开始执行");
// 模拟耗时操作
Task.Delay(2000).Wait();
Console.WriteLine("任务执行完成");
});
Console.WriteLine("主线程继续执行");
}
2、带返回参数的表达
- 可以看到下面代码中,定义了 Task 的类型为整形int作为返回值,因为我们调用的函数Sum()返回值就是int。
- 这里用了比较标准的写法,也就是Task.Run + async/await的写法,async和await必须是同时出现的。
- 等待任务完成并获取结果的"int result = task.Result;"这句话非常关键,没有这句话就不会等待2秒后的任务完成获取返回值,程序则会继续运行,result将得不到计算结果,result将输出结果为0。
c
//调用
private void MyTask()
{
Console.WriteLine("任务开始");
int a=1;int b=2;int result =0;
// 任务返回 int 类型结果
Task<int> task = Task.Run(() => Sum(a,b));
// 等待任务完成并获取结果//关键
result = task.Result;
Console.WriteLine("结果:" + result); // 输出 3
}
//任务
private async int Sum(int A,int B)
{
//模拟任务时间2秒
await Task.Delay(2000);
//返回值
return (A+B);
}
10:33:54:608 任务开始
10:33:56:613 任务内结果:3
10:33:56:613 结果:3
3、无返回值等待任务结束
- 如果没返回值,一般情况是继续往下执行,以达到多线程异步处理,也就是不等待任务完成父线程继续往下执行,如果我们一样是需要等待呢?那,那,那也是可以实现的( ̄▽ ̄) ,运行任务后没有返回值等待,那我们就等待整个任务,await task等待任务运行结束。需要注意的是必须用async修饰定义该函数。
- 可以看到到输出运行结果同前面的输出结果是一样的,我们换成了全局变量(R)来接收结果。
csharp
//全局变量
int R = 0;
//调用
private async void MyTask()
{
Console.WriteLine("任务开始");
int a = 1; int b = 2;
// 任务返回 int 类型结果,不获取返回值
Task task = Task.Run(() => Sum(a, b));
await task;//等待运行结束
//等待任务完成后打印
Console.WriteLine("结果:" + R); // 输出 3
}
//任务
private async Task<int> Sum(int A, int B)
{
//模拟任务时间2秒
await Task.Delay(2000);
R = A+B;
// 输出结果
Console.WriteLine("任务内结果:" + R);
return R;
}
输出
10:31:31:103 任务开始
10:31:33:092 任务内结果:3
10:31:33:092 结果:3
4、停止业务
- 需要控制任务的停止,可以新建一个取消令牌源,可以叫它取消开关或标志。
- 在调用任务前我可以定义一个毫秒时间来控制器停止,实例中给了1秒(1000),通过输出内容我们可以看到其停止的效果,和线程中停止线程循环的效果基本一样。
csharp
//新建取消令牌源
CancellationTokenSource cts = new CancellationTokenSource();
//调用
private async void MyTask()
{
int a = 1; int b = 2;
// 1秒后取消任务
cts.CancelAfter(1000);
Console.WriteLine("任务开始");
// 任务返回 int 类型结果
Task<int> task = Task.Run(() => Sum(a, b),cts.Token);
//等待并读取结果
int result = task.Result;
//输出打印任务结果
Console.WriteLine("结果:" + result);
}
//任务
private async Task<int> Sum(int A, int B)
{
int R = 0;
//查看标志循环
while (!cts.Token.IsCancellationRequested)
{
//模拟循环
await Task.Delay(100);
R += A + B;
Console.WriteLine("任务内结果:" + R);
}
return R;
}
输出
10:20:52:514 任务开始
10:20:52:777 任务内结果:3
10:20:52:777 任务内结果:6
10:20:53:025 任务内结果:9
10:20:53:025 任务内结果:12
10:20:53:265 任务内结果:15
10:20:53:265 任务内结果:18
10:20:53:265 任务内结果:21
10:20:53:522 任务内结果:24
10:20:53:522 任务内结果:27
10:20:53:522 结果:27
4、其他功能和状态
- 其它功能和状态获取简单介绍一下。
(1) 等待任务
csharp
task.Wait(); // 等待单个任务
Task.WaitAll(t1, t2); // 等待所有任务完成
Task.WaitAny(t1, t2); // 等待任意一个完成
(2)获取状态
csharp
task.IsCompleted // 是否完成
task.IsFaulted // 是否出错
task.IsCanceled // 是否取消
task.Status // 详细状态
三、总结
- C#中的Task任务是个高级的多线程异步操作方案,优先用 Task.Run + async/await的格式来描述;
- 注意await task和task.Wait()是有区别的,await task不阻塞线程,异步等待,线程可以去干别的,而task.Wait()阻塞线程,死等,线程卡死在这里不动。
- 任务对比Thread线程 更轻量、高效、易维护,还支持返回值、等待、取消等等操作,这么高级的任务功能,大家可以用起来哟!