• .NET异步编程模式(四)-TAP


    TAP 是基于任务的异步模式,在 .NET Framework 4 中引入。TAP取代了 APM 和EAP,是推荐的异步编程模式。

    async / await

    async 和 await 是为异步编程提供的语法糖,方便我们快捷编写异步代码。关键字 async 作用仅仅是为了能够使用 await 关键字以及怎么处理返回值。await 关键字可以想象成 asynchronous wait,在awaitable 完成之前,异步方法会等待,但线程不会堵塞。

    public async Task DoSomethingAsync()
    {
        // For this example, we`re just going to (aynchronously) wait 100ms.
        await Task.Delay(100);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对于调用方法,await 声明了一个挂起点,等异步方法结束后会捕获当前上下文继续执行后续代码。、

    awaitable

    await 就像是一元操作符,接收一个参数 - awaitable. Task 和 Task 都是这样的类型。

    public async Task NewStuffAsync()
    {
        // Use await and have fun with the new stuff.
        await ...
    }
    public Task MyOldTaskParallelLibraryCode()
    {
        // Note that this is not an async method, so we can`t use await in here.
        ...
    }
    public async Task ComposeAsync()
    {
        // We can await Tasks, regardless of where they come from.
        await NewStuffAsync();
        await MyOldTaskParallelLibraryCode();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Task.Yield()

    await Task.Yield() 方法来强制异步完成方法,可以让我们更好的控制异步方法的执行。如果当前任务很耗时,并且优先级比较低,可以考虑在方法开始的时候加上 await Task.Yield() ,让系统去调度其他更需要的任务,稍后再来完成该耗时任务。

    static async Task Process()
    {
        await Task.Yield();
    
        var tcs = new TaskCompletionSource<bool>();
    
        Task.Run(() =>
        {
            Thread.Sleep(1000);
            tcs.SetResult(true);
        });
    
        tcs.Task.Wait();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我不着急,我到后面从新排队去,你先去处理其他任务吧。其实是利用 await 实现线程的切换

    Task.ConfigureAwait

    默认情况,异步方法结束后会捕获和回复当前上下文。如果你不关系延续上下文,可以使用 Task.ConfigureAwait 指示不要回复而是继续执行等待的任务。

    await someTask.ConfigureAwait(continueOnCapturedContext:false);
    
    • 1

    CancellationTokenSource

    从 .NET Framework 4 开始,TAP 方法支持取消操作。

    var cts = new CancellationTokenSource();
    string result = await DownloadStringTaskAsync(url, cts.Token);// at some point later, potentially on another thread
    cts.Cancel();
    
    // 取消多个异步调用
    var cts = new CancellationTokenSource();
    IList<string> results = await Task.WhenAll(from url in urls select DownloadStringTaskAsync(url, cts.Token));
    // at some point later, potentially on another thread
    …
    cts.Cancel();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Progress

    通过 Progress 可以监控异步方法的执行进度。

    private async void btnDownload_Click(object sender, RoutedEventArgs e)
    {
        btnDownload.IsEnabled = false;
        try
        {
            txtResult.Text = await DownloadStringTaskAsync(txtUrl.Text,
                new Progress<int>(p => pbDownloadProgress.Value = p));
        }
        finally { btnDownload.IsEnabled = true; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Task.Run

    Task.Run() 方法可以很方便的将耗时任务放到线程池上执行。

    public async void button1_Click(object sender, EventArgs e)
    {
        // 默认恢复上下文
        textBox1.Text = await Task.Run(() =>
        {
            // … do compute-bound work here
            return answer;
        });
    }
    
    public async void button1_Click(object sender, EventArgs e)
    {
        // 内部使用 await
        pictureBox1.Image = await Task.Run(async() =>
        {
            using(Bitmap bmp1 = await DownloadFirstImageAsync())
            using(Bitmap bmp2 = await DownloadSecondImageAsync())
            return Mashup(bmp1, bmp2);
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Task.FromResult

    Task.FromResult 用来创建一个带返回值的,已完成的 Task。

    public Task<int> GetValueAsync(string key)
    {
        int cachedValue;
        return TryGetCachedValue(out cachedValue) ?
            Task.FromResult(cachedValue) :              // 如果本地有缓存,直接以同步的方式获取(但返回的是异步结果)
            GetValueAsyncInternal();                    // 如果本地没有key对应的缓存,则异步从远端获取
    }
    // 异步方法从远端获取缓存
    private async Task<int> GetValueAsyncInternal(string key)
    {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Task.WhenAll

    异步等待 一组异步操作的完成。

    Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray();
    try
    {
        await Task.WhenAll(asyncOps);
    }
    catch(Exception exc)
    {
        foreach(Task faulted in asyncOps.Where(t => t.IsFaulted))
        {// work with faulted and faulted.Exception
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Task.WhenAny

    一组异步操作中,第一个异步操作完成时返回。

    1. 可以同时进行多个相同的异步操作,选择最快完成的那个
    // 从多个行情源处获取行情,使用最快的那个
    var cts = new CancellationTokenSource();
    var recommendations = new List<Task<bool>>()
    {
        GetBuyRecommendation1Async(symbol, cts.Token),
        GetBuyRecommendation2Async(symbol, cts.Token),
        GetBuyRecommendation3Async(symbol, cts.Token)
    };
    
    Task<bool> recommendation = await Task.WhenAny(recommendations);
    cts.Cancel(); // 取消剩余任务
    if (await recommendation) BuyStock(symbol);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 多个任务交叉进行(每完成一个就处理一个)
    List<Task<Bitmap>> imageTasks =
        (from imageUrl in urls select GetBitmapAsync(imageUrl)
             .ContinueWith(t => ConvertImage(t.Result)).ToList();
    while(imageTasks.Count > 0)
    {
        try
        {
            Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
            imageTasks.Remove(imageTask);
    
            Bitmap image = await imageTask;
            panel.AddImage(image);
        }
        catch{}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Task.Delay

    在异步方法中暂定一段时间。 可以和 Task.WhenAny ,Task.WhenAll 结合使用以实现超时处理。

    public async void btnDownload_Click(object sender, EventArgs e)
    {
        btnDownload.Enabled = false;
        try
        {
            Task<Bitmap> download = GetBitmapAsync(url);
            if (download == await Task.WhenAny(download, Task.Delay(3000)))
            {
                Bitmap bmp = await download;
                pictureBox.Image = bmp;
                status.Text = "Downloaded";
            }
            else
            {
                pictureBox.Image = null;
                status.Text = "Timed out";
                var ignored = download.ContinueWith(
                    t => Trace("Task finally completed"));
            }
        }
        finally { btnDownload.Enabled = true; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    公众号 - 希夏普

  • 相关阅读:
    QML SplitView
    大数据之Hive基本查询
    【MATLAB源码-第67期】基于麻雀搜索算法(SSA)的无人机三维地图路径规划,输出最短路径和适应度曲线。
    快手小程序模板_快手小程序模板平台制作
    《C++避坑神器·二十》C++智能指针简单使用
    从rocketmq入手,解析各种零拷贝的jvm层原理
    【web-攻击用户】(9.1.6)查找并利用XSS漏洞--基于DOM
    Spring Boot 版本 GA、RC、beta等含义
    vue2中,vue-easytable组件的使用(三)——实现表格的虚拟滚动功能
    使用Excel导入和导出数据
  • 原文地址:https://blog.csdn.net/jqwang_2009/article/details/126421277