文章目录
前言
Task是对于Thread的封装,是极其优化的设计,更加方便了我们如何操控线程
Task
Task声明形如:
public static Task Sleep()
{
}
Task是一种类型
返回值
直接声明Task是需要返回值的。
无参返回
public static Task Sleep(int second)
{
return Task.CompletedTask;
}
有参返回
public static Task<T> Sleep()
{
return Task.FromResult(T);
}
//举例,返回参数只能唯一,除非使用元祖
public static Task<string> Sleep()
{
return Task.FromResult("Hello world!");
}
使用Task.Result获取返回值
var res = Sleep().Result;
async和await
async和await是对于异步事件的控制,方便我们对异步事件的操控。
返回值
使用async之后可以直接设置返回值
///有参返回
public static async Task<string> Sleep()
{
return "Hello world";
}
///无参返回
public static async Task Sleep()
{
}
await搭配使用
异步事件的等待使用await方法
public static async Task Sleep(int second)
{
await Task.Delay(second * 1000);
Console.WriteLine($"等待{second}s");
}
static void Main(string[] args)
{
Sleep(3);
Sleep(2);
Sleep(1);
Console.WriteLine("运行完毕");
//使用键盘键入事件阻塞主进程,主进程结束程序会立即退出
Console.ReadKey();
}
打印结果:
打印结果显示:
- 同步事件先结束
- 异步事件互相不阻塞,3,2,1同时开始,等待3,2,1s打印1,2,3。
Main async改造
主程序是Void,无法等待
将Void改成Task,即可等待异步事件
打印结果服务预期,等待异步事件结束后运行
Task进阶
C#Task取消任务执行CancellationTokenSource
C# Task 暂停与取消
Task线程取消
以前Thread有Abort()方法,强行销毁线程,但是这个方法用于极大的安全问题,已经被弃用。
线程不能直接被销毁,只能通过抛出异常来取消线程。
//声明token
var tokenSource = new CancellationTokenSource();
//注册异常抛出
tokenSource.Token.ThrowIfCancellationRequested();
//注册取消事件回调
tokenSource.Token.Register(() =>
{
Console.WriteLine("线程已被取消");
});
。。。。。。别的地方的代码
//取消token,那么之前写ThrowIfCancellationRequested的地方会直接结束
tokenSource.Cancel();
测试用例
一个简单的死循环函数,运行时返回token,用于直接跳出程序
static async Task Main(string[] args)
{
var token = Loop();
//等待3s抛出异常
await Task.Delay(1000 * 3);
Console.WriteLine("任务完成!");
token.Cancel();
Console.ReadKey();
}
/// <summary>
/// 循环等待
/// </summary>
/// <returns></returns>
public static CancellationTokenSource Loop()
{
var tokenSource = new CancellationTokenSource();
Console.WriteLine("任务开始!");
tokenSource.Token.Register(() =>
{
Console.WriteLine("线程已被取消");
});
var count = 0;
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
//抛出异常,直接结束线程
tokenSource.Token.ThrowIfCancellationRequested();
count++;
Console.WriteLine(count);
}
});
return tokenSource;
}
打印结果
这样使用起来也更加安全。
超时设置
tokenSource.CancelAfter是超时方法。
CancelAfter(1000):1000毫秒后超时
static async Task Main(string[] args)
{
var token = Loop();
///3000毫秒后取消
token.CancelAfter(1000*3);
Console.ReadKey();
}
/// <summary>
/// 循环等待
/// </summary>
/// <returns></returns>
public static CancellationTokenSource Loop()
{
var tokenSource = new CancellationTokenSource();
Console.WriteLine("任务开始!");
tokenSource.Token.Register(() =>
{
Console.WriteLine("线程已被取消");
});
var count = 0;
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
tokenSource.Token.ThrowIfCancellationRequested();
count++;
Console.WriteLine(count);
}
});
return tokenSource;
}
线程暂停和继续
线程暂停也是使用一个类去控制,ManualResetEvent。和线程销毁一样,是不能直接暂停的,因为直接暂停也不安全。
//声明,false为默认阻塞,true为不阻塞
var resetEvent = new ManualResetEvent(false);
//暂停,通过WaitOne方法来阻塞线程,通过Set和Reset来设置是否阻塞
resetEvent.WaitOne();
//不阻塞
resetEvent.Set()
//阻塞
resetEvent.Reset()
测试用例
static async Task Main(string[] args)
{
var canStop = CanStop();
//等待3s抛出异常
Console.WriteLine("等待3s启动");
await Task.Delay(1000 * 3);
Console.WriteLine("启动!");
canStop.Set();
Console.WriteLine("等待3s暂停");
await Task.Delay(3000);
Console.WriteLine("暂停!");
canStop.Reset();
Console.ReadKey();
}
public static ManualResetEvent CanStop()
{
var resetEvent = new ManualResetEvent(false);
var count = 0;
Task.Run(async () =>
{
while (true)
{
resetEvent.WaitOne();
await Task.Delay(1000);
count++;
Console.WriteLine(count);
}
});
return resetEvent;
}
多任务等最快
await Task.WhenAny(Task1,Task2,Task3)
只会等待最快的一个。
static async Task Main(string[] args)
{
await Task.WhenAny(Sleep(1),Sleep(2),Sleep(3));
Console.WriteLine("运行结束");
Console.ReadKey();
}
public async static Task Sleep(int second)
{
await Task.Delay(second*1000);
Console.WriteLine($"等待{second}s");
}
运行结果
多任务全等待
static async Task Main(string[] args)
{
await Task.WhenAll(Sleep(1), Sleep(2), Sleep(3));
Console.WriteLine("运行结束");
Console.ReadKey();
}
public async static Task Sleep(int second)
{
await Task.Delay(second*1000);
Console.WriteLine($"等待{second}s");
}
结论
异步线程的控制是极其重要的内容,Task还可以和委托一起使用,对程序的运行有更强的把控力。