1. 异步调用链中会切换线程
using System.Runtime.CompilerServices;
namespace ConsoleApp1
{
public class Program
{
public async static Task Main(string[] args)
{
Helper.PrintThreadId("Before");
await FooAsync();
Helper.PrintThreadId("After");
}
async static Task FooAsync()
{
Helper.PrintThreadId("Before");
await Task.Delay(1000);
Helper.PrintThreadId("After");
}
}
class Helper
{
private static int index = 1;
public static void PrintThreadId(string? msg = null, [CallerMemberName] string? caller = null)
{
var title = $"{index}: {caller}";
if (!string.IsNullOrEmpty(msg)) title += $" @ {msg}";
var id = Environment.CurrentManagedThreadId;
Console.WriteLine($"{title}, {id}");
Interlocked.Increment(ref index);
}
}
}
从结果可以看出异步调用前后线程有可能会切换
2. 配置Task是否回到之前的线程ConfigureAwait
配置任务通过await方法结束扣是否回到原来的线程,默认为true
一般只有UI线程会采用这种策略
public class Program
{
public async static Task Main(string[] args)
{
Helper.PrintThreadId("Before");
var ret = await FooAsync().ConfigureAwait(false);//这里控制台是没效果的,要放在WPF或Winform程序中测试才有效果
//如果后面有其他操作UI的操作,会报错,因为此时不是在UI线程了
Helper.PrintThreadId("After");
}
async static Task<int> FooAsync()
{
Helper.PrintThreadId("Before");
await Task.Delay(1000);
Helper.PrintThreadId("After");
return 10;
}
}
3. 异步的取消
var cts = new CancellationTokenSource();
var task = Task.Delay(10000,cts.Token);
Thread.Sleep(2000);//过2秒种异步结束了,但这里会抛异常
cts.Cancel();
await task;
正确做法:
var cts = new CancellationTokenSource();
try
{
var task = Task.Delay(10000, cts.Token);
Thread.Sleep(2000);
cts.Cancel();
await task;
}
catch(TaskCanceledException)
{
"Task Canceled".Dump();
}
finally
{
cts.Dispose();
}
4. 异步编程中可用的同步机制
目前只知道这个SemaphoreSlim可以异步中用于Task同步
var inputs = Enumerable.Range(1,10).ToArray();
var sem = new SemaphoreSlim(1,1);//设置每次只有一个通过
var tasks = inputs.Select(HeavyJob).ToList();
await Task.WhenAll(tasks);
var outputs = tasks.Select(x => x.Result).ToArray();
outputs.Dump();
async Task<int> HeavyJob(int input)
{
await sem.WaitAsync();
await Task.Delay(1000);
sem.Release();
return input*input;
}
可从结果中得到最后差不多10s完成任务
改成var sem = new SemaphoreSlim(2,2);//每次可通过2个,猜测完成任务应该5s左右