前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >使用 Task 实现提前加载

使用 Task 实现提前加载

作者头像
jgrass
发布2024-12-25 16:01:30
发布2024-12-25 16:01:30
7800
代码可运行
举报
文章被收录于专栏:蔻丁杂记蔻丁杂记
运行总次数:0
代码可运行

介绍一种/两种可以提前做点什么事情的方法。

场景

在 UI 线程中执行耗时操作,如读取大文件,为了不造成 UI 卡顿,常采用异步加载的方式,即 async/await 。 通常的写法是这样的:

代码语言:javascript
代码运行次数:0
运行
复制
private async Task DoSomething(){    // init work    await Task.Run(()=>    {        // IO    });    // after work}

问题与需求

这里虽然解决了UI卡顿的问题,但需要得到最终结果(即 after work 中的代码执行),仍然需要等待。

在部分场景中,如果可以提前执行耗时代码,则可以减少等待时间。

设想的代码类似与这样:

代码语言:javascript
代码运行次数:0
运行
复制
private void EarlierWork(){    // 提前执行耗时操作,如从一个大文件中查找数据。}
priavte void DoSomething(){    // init    // 获取之前提前执行的结果,如果执行还没有结束,则异步等待执行完成,获取结果后继续执行。    // after work}

这里适用的场景,需要是 EarlierWorkDoSomething 先执行一段时间,而且在 EasilerWork 执行时,就已经可以获取相关的数据,不缺失必要的参数。(这样的场景……额,可能并不多)

实现

这里的实现有两种方式,都是基于 C# 的 async/await 异步模型。

实现一 awaiter

代码语言:javascript
代码运行次数:0
运行
复制
using System.Runtime.CompilerServices;using System.Threading.Tasks;
private TaskAwaiter<string> _getStringDataAwaiter;
private void EarlierWork(){    // 提前执行耗时操作,如从一个大文件中查找数据。    _getStringDataAwaiter = Task.Run(()=>    {        // 耗时操作        return "Data";    }).GetAwaiter();}
private async Task DoSomething(){    // init    // 获取之前提前执行的结果,如果执行还没有结束,则异步等待执行完成,获取结果后继续执行。    string data = await Task.Run(()=>_getStringDataAwaiter.GetResult());    // after work}

注意,这里需要使用 Task.Run() 的方式调用获取结果的 GetResult 方法,否则会是调用线程卡顿,即,如果调用线程是UI,则会造成UI卡顿。

需要注意的是,TaskAwaiter 这个API是提供给编译器使用的,不建议在生产环境中使用

The System.Runtime.CompilerServices namespace provides functionality for compiler writers who use managed code to specify attributes in metadata that affect the run-time behavior of the common language runtime. This API supports the product infrastructure and is not intended to be used directly from your code. TaskAwaiter Struct (System.Runtime.CompilerServices) | Microsoft Docs

实现二

推荐的实现方式。

代码语言:javascript
代码运行次数:0
运行
复制
using System.Threading.Tasks;
private Task<string> _getStringDataTask;
private void EarlierWork(){    // 提前执行耗时操作,如从一个大文件中查找数据。    _getStringDataTask = CreateGetDataTask();}
private async Task<string> CreateGetDataTask(){    return await Task.Run(()=>    {        // 耗时操作        return "Data";    }).ConfigureAwait(false);  // 必须写 ConfigureAwait(false)
    // 此处的代码将不会返回原线程执行;不过这里一般不写代码。}
private void DoSomething(){    // init    // 获取之前提前执行的结果,如果执行还没有结束,则异步等待执行完成,获取结果后继续执行。    string data = _getStringDataTask.Result;    // after work}

这种实现方式需要注意死锁问题,如果不使用 ConfigureAwait(false) ,则会造成死锁。

关于死锁的更多内容,可以看这里:

一个神奇的异步转同步的方式

有如下一个 GetDataAsync 方法,使用了 async/await ,所以调用方也必须使用 async/await 的方式,不然就失去了同步的特性。

问题是,有时候 async/await 引起的病毒传播所带来的改动,可能会很大,尤其对于旧代码。

如何实现一种诡异的调用方法,来调用这种方法呢?

代码语言:javascript
代码运行次数:0
运行
复制
public class Work{    public async Task<bool> GetDataAsync()    {        return await Task.Run(() =>        {            return false;        });    }}

诡异代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
public class Work{    public bool GetData()    {        Task<bool> getDataTask = CreateGetDataTask();        return getDataTask.Result;    }
    // private    private async Task<bool> CreateGetDataTask()    {        return await Task.Run(async () =>        {            return await GetAvaiableAsync();        }).ConfigureAwait(false);  // 必须写 ConfigureAwait(false)
        // 使用下面这段代码会死锁        // return await GetAvaiableAsync().ConfigureAwait(false);    }
}

这样,外部就可以调用 GetData 这个很普通的方法,而不用调用 GetDataAsync 这个方法了。

其实就是上面提到的实现方法二,

但这里需要注意的是,GetData 方法会引发线程阻塞,如果在 UI 线程调用,则会卡 UI,非特殊情况,不建议这么使用。

即使使用,也不要将 GetData 作为一般方法对外公开。

关于异步转同步,可参见:

将 async/await 异步代码转换为安全的不会死锁的同步代码(使用 PushFrame) - walterlv

补充:

其实使用 TaskCompletionSource 可以实现一样的效果,当然,一样需要注意使用 ConfigureAwait(false),不然也会带来死锁问题。


原文链接: https://cloud.tencent.com/developer/article/2481432

本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年6月16日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景
  • 问题与需求
  • 实现
    • 实现一 awaiter
    • 实现二
    • 一个神奇的异步转同步的方式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档