C# 中使用 Task 实现提前加载
介绍一种/两种可以提前做点什么事情的方法。
场景
在UI线程中执行耗时操作,如读取大文件,为了不造成UI卡顿,常采用异步加载的方式,即 async/await 。
通常的写法是这样的:
private async Task DoSomething()
{
// init work
await Task.Run(()=>
{
// IO
});
// after work
}
问题与需求
这里虽然解决了UI卡顿的问题,但需要得到最终结果(即 after work 中的代码执行),仍然需要等待。
在部分场景中,如果可以提前执行耗时代码,则可以减少等待时间。
设想的代码类似与这样:
private void EarlierWork()
{
// 提前执行耗时操作,如从一个大文件中查找数据。
}
priavte void DoSomething()
{
// init
// 获取之前提前执行的结果,如果执行还没有结束,则异步等待执行完成,获取结果后继续执行。
// after work
}
这里适用的场景,需要是 EarlierWork 比 DoSomething 先执行一段时间,而且在 EasilerWork 执行时,就已经可以获取相关的数据,不缺失必要的参数。(这样的场景……额,可能并不多)
实现
这里的实现有两种方式,都是基于 C# 的 async/await 异步模型。
实现一: awaiter
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
实现二:
推荐的实现方式。
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 引起的病毒传播所带来的改动,可能会很大,尤其对于旧代码。
如何实现一种诡异的调用方法,来调用这种方法呢?
public class Work
{
public async Task<bool> GetDataAsync()
{
return await Task.Run(() =>
{
return false;
});
}
}
诡异代码如下:
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://www.cnblogs.com/jasongrass/p/10645566.html
END
C# 中使用 Task 实现提前加载的更多相关文章
- Java 构造函数(抽象类中的构造函数) 和 加载
博客分类: 面向对象设计的原则 与 概念 1. Java 的构造函数 与初始化块: a. 抽象类的构造函数 若果在父类中(也就是抽象类)中显示的写了有参数的构造函数,在子类是就必须写一个构造函数来 ...
- Spring提前加载与懒加载
首先,Spring默认是提前加载,这意味着当项目启动,spring初始化,spring会把所有的扫描包下的 ,所有带spring 注解(@Component.@Repository.@Service. ...
- sqlalchemy 实体属性提前加载
在flask里需要给视图传送数据,肯定需要把模型的实体属性提前加载,可以使用 sqlalchemy.orm.subqueryload 或 sqlalchemy.orm.joinedload 示例: @ ...
- EXT中的iconCls 图标加载
刚刚遇到了个奇怪的问题. 我用 在主页面用TAB autoLoad:{url:link, nocache: true, scripts:true} 加载页面Student.jsp, 郁闷,FF可以正常 ...
- java_有返回值线程_提前加载例子
package com.demo.test3; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionE ...
- MVC中实现部分内容异步加载
MVC中实现部分内容异步加载 action中定义一个得到结果集的方法 public ActionResult GetItemTree(string title, int itemid, int? pa ...
- 在前端页面对easyui中的datagrid与jqgrid加载后的数据进行操作
因为项目的需求,需要在grid中加载数据后再在前端页面执行操作,所以在easyui中的grid与jqgrid都进行了测试和操作: eayui中grid数据的操作: //构造集合对象 var list ...
- 深入浅出经典面试题:从浏览器中输入URL到页面加载发生了什么 - Part 3
备注: 因为文章太长,所以将它分为三部分,本文是第三部分. 第一部分:深入浅出经典面试题:从浏览器中输入URL到页面加载发生了什么 - Part 1 第二部分:深入浅出经典面试题:从浏览器中输入URL ...
- 从浏览器中输入URL到页面加载的发生了什么-转载
转:https://www.cnblogs.com/confach/p/10050013.html 背景 “从浏览器中输入URL到页面加载的发生了什么“,这是一道经典的面试题,涉及到的知识面非常多,但 ...
随机推荐
- PHP远程连接mysql报错处理办法
在万网弄了个虚拟主机,想远程连mysql调试(本地4G如果开mysql内存不够!),一直报错: mysqlnd cannot connect to MySQL 4.1+ using the old i ...
- ECMAScript6语法检查规范错误信息说明
项目中使用ECMAScript6的时候经查会使用语法检查,下面是常见错误信息的汇总: “Missing semicolon.” : “缺少分号.”, “Use the function form of ...
- 发现IE 9的一个独有的小bug,并附解决方案
在最近的项目中,解决了一些浏览器兼容方面的bug,这篇主要描述在IE 9在渲染值为auto的overflow-x属性时,所产生的专属bug及解决办法. 1.问题描述 在做一个收货地址管理静态页面的时候 ...
- React Native新手入门
前言 React Native是最近非常火的一个话题,想要学习如何使用它,首先就要知道它是什么. 好像面对一个新手全面介绍它的文章还不多,我就归纳一下所有的资料和刚入门的小伙伴一起来认识它~ 将从以下 ...
- 移动端测试=== adb 无线连接手机
无线连接(需要借助 USB 线) 除了可以通过 USB 连接设备与电脑来使用 adb,也可以通过无线连接——虽然连接过程中也有需要使用 USB 的步骤,但是连接成功之后你的设备就可以在一定范围内摆脱 ...
- Nginx实现代理和用户验证
1.下载Nginx 首先去官网http://nginx.org/en/download.html下载需要的版本即可,无需安装,只需要打开nginx.exe文件,nginx.exe的服务就开启了.打开h ...
- js实现数据视图双向绑定原理
这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.observe也被草案发起人撤回了..所以defineProperty更有必要了解一下了几行代码看他 ...
- bootstrap File Input 多文件上传插件使用记录(二)删除原文件
在上一篇文章中,主要介绍了file input插件的初始化和多文件同步上传到服务器的相关配置等.这篇主要介绍file input插件的编辑等. 使用场景: 在后台管理框架中,一条数据中包含不固定的多张 ...
- javaScript一些需要注意的细节
变量声明早于代码运行. 函数声明早于变量声明. this指针代表的是执行当前代码的对象的所有者. JavaScript执行完同步,才能执行异步队列.如:alert,for if while 同步执行, ...
- Centos之字符串搜索命令grep
grep [选项] 字符串 文件名 在文件当中匹配符合条件的字符串 选项: -i 忽略大小写 -v 排除指定字符串 [root@localhost ~]# grep "work" ...