C#异步函数

 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左右

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值