异步编程
1.多线程
1.1.Thread类
Thread类的基本使用
//和java差不多,传入一个函数给委托
var thread= new Thread(()=>
{
Consoel.WriteLine("do sth...");
});
//开始异步执行线程
thread.Start();
//线程睡眠
thread.Sleep(1000);
//让主线程等待
thread.Join();
//中止线程
try
{
thread.Interrupt();
}
catch (ThreadInterruptedException e)
{
Console.WriteLine(e);
throw;
}
2.异步编程
2.1.Task类
task默认使用线程池进行通信,由系统自动分配线程异步执行
//创建一个新的线程并执行
var task = new Task(() =>
{
Console.WriteLine("do sth")
}
);
task.Start();
2.2.async和await
async会将当前方法定义为异步方法,在编译时加入状态机进行线程的切换。
awaite会将当前函数线程挂起,线程空闲时会执行其他任务。
awaite只能在async方法中使用
//代码参考下面
2.3.使用channel进行通信
internal class Program
{
private static async Task Main(string[] args)
{
//创建一个channel
var channle = Channel.CreateUnbounded<Student>();
//等待发送发发送完成
await SendInfo(channle.Writer);
//将发送状态设置为完成
channle.Writer.Complete();
//接收方读取信息
await ReadInfo(channle.Reader);
return;
}
//消息发送的异步方法
static async Task SendInfo(ChannelWriter<Student> writer)
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(200);
await writer.WriteAsync(new Student { UserId = i, UserName = $"张三{i}" });
System.Console.WriteLine($"{Environment.CurrentManagedThreadId}--发送消息--{Environment.CurrentManagedThreadId}--{i}");
}
}
//消息接收的异步方法
static async Task ReadInfo(ChannelReader<Student> reader)
{
await foreach (var item in reader.ReadAllAsync())
{
System.Console.WriteLine($"{Environment.CurrentManagedThreadId}--读取到内容{item.UserId}--{item.UserName}");
}
}
}
public class Student
{
public long UserId;
public string? UserName;
}
2.4.在同步方法中调用异步方法
- GetAwaiter().getResult();
- 使用弃元 _=FooAsync();
- 使用void返回值的异步方法
具体代码如下:
internal class Program
{
public static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
Console.WriteLine("程序开始");
//阻塞当前线程直到获取到值,常用
FooAsync().GetAwaiter().GetResult();
//使用fire and forget调用(弃元),无法获取到异常信息
_ = FooAsync();
//使用void返回值的异步方法,抛出异常可能会中止程序
VoidFooAsync();
for (int i = 0; i < 5; i++)
{
Thread.Sleep(200);
Console.WriteLine($"{Environment.CurrentManagedThreadId}-当前值为: {i}");
}
Console.WriteLine($"程序耗时:{sw.ElapsedMilliseconds}ms");
Console.ReadKey();
}
static async Task FooAsync()
{
await Task.Delay(200);
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{Environment.CurrentManagedThreadId}-当前值为: {i}");
}
}
static async void VoidFooAsync()
{
await Task.Delay(200);
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{Environment.CurrentManagedThreadId}-当前值为: {i}");
}
throw new Exception();
}
}
2.5.使用Cancellation来取消异步任务
创建一个CancellationTokenSource并传入变量,调用其Cancel方法时会取消任务并触发TaskCanceledException异常
var cts = new CancellationTokenSource();
//取消任务
cts.Cancel();
await Task.Delay(2000,cts.Token);
Console.WriteLine("任务已取消");
可以设置一段时间后自动取消任务。
两秒钟后任务会被取消,注意捕获异常。
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
await Task.Delay(3000,cts.Token);
Console.WriteLine("任务已取消");
异常处理
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
cts.Cancel();
try
{
await Task.Delay(2000, cts.Token);
}
catch (TaskCanceledException e)
{
Console.WriteLine(e);
throw;
}
finally
{
cts.Dispose();
}
Console.WriteLine("任务已取消");
2.6.设置任务超时
//设置一个delay任务,如果这个任务先执行完则任务没有超时,否则任务超时
//执行任务a
var taskA = new Task(() => { Console.WriteLine("这是一个任务"); });
taskA.Start();
//等待任务执行
var taskB = await Task.WhenAny(taskA,Task.Delay(2000));
//如果先执行完的不是任务a,则任务超时了
if (taskB != taskA )
{
Console.WriteLine("你的任务超时了,做点什么吧");
}