async/await Task Timeout
async/await Task Timeout
在日常的电脑使用过程中,估计最难以忍受的就是软件界面“卡住”“无响应”,在我有限的开发生涯中一直都是在挑战
它。在WPF中,主线程即UI线程,当我们在UI线程中执行一个很耗时的操作,以至于UI线程没能继续绘制窗体,这时给人
的感觉就是“卡住”了。
很耗时的操作分为2种
复杂计算
I/O操作
为了有一个良好的用户操作体验,我们都会使用异步方法,在另外一个线程中处理耗时的操作,当操作结束时,仅仅使用
UI线程更新结果到界面。.Net中的异步模型也有很多种,园子里有很多,不过用起来很舒服的还是async/await。
async/await 的引入让我们编写异步方法更加容易,它的目的就是使得我们像同步方法一样编写异步方法。上面铺垫稍微
啰嗦了点。马上进入正题,当我们在await一个方法时,如果这个方法它是支持超时的,那么当超时时是以异常的形式来
通知我们的,这样await以下的方法就没有办法执行了。
注意:这里补充下,一个Task超时了,并不意味着这个Task就结束了,它还是会运行,直到结束或是发生异常,一个超 时的Task返回的结果不应该被继续使用,应该丢弃
提供了超时设置还好,但是如果这个方法没有超时设置,那岂不就是一直在这里傻等?那肯定不,只有自己实现超时,一个
线程是没有办法做超时功能的。一般都是一个线程执行耗时操作,一个线程来计算超时,并且超时了要以异常的形式通知出来,所以
代码应该是这样的:
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
try
{
await CanTimeoutTask(LongTimeWork, 6000);
textBlock.Text = "XXD";
await CanTimeoutTask(LongTimeWork, 3000);
}
catch (Exception ex)
{
}
}
private async Task CanTimeoutTask(Action action, int timeout)
{
var task1 = Task.Run(action);
var task2 = Task.Delay(timeout);
var firstTask = await Task.WhenAny(task1, task2);
if (firstTask == task2)
{
throw new TimeoutException();
}
}
private void LongTimeWork()
{
Thread.Sleep(5000);
}
如此看来,已经满足我们的需求了,但是作为一个上进的程序员,这么写真累啊,能不能提出一个简单易用的方法出来,
于是上Bing(这两天有点厌恶百度)搜索,看到这么一篇好像有点意思,自己琢磨着改进了一下,所以有了如下版本:
/// <summary>
/// 无返回值 可超时,可取消的Task
/// </summary>
public class TimeoutTask
{
#region 字段
private Action _action;
private CancellationToken _token;
private event AsyncCompletedEventHandler _asyncCompletedEvent;
private TaskCompletionSource<AsyncCompletedEventArgs> _tcs;
#endregion
#region 静态方法
public static async Task<AsyncCompletedEventArgs> StartNewTask(Action action, CancellationToken token)
{
return await TimeoutTask.StartNewTask(action, token, Timeout.Infinite);
}
public static async Task<AsyncCompletedEventArgs> StartNewTask(Action action, int timeout)
{
return await TimeoutTask.StartNewTask(action, CancellationToken.None, timeout);
}
public static async Task<AsyncCompletedEventArgs> StartNewTask(Action action, CancellationToken token,
int timeout = Timeout.Infinite)
{
var task = new TimeoutTask(action, token, timeout);
return await task.Run();
}
#endregion
#region 构造
protected TimeoutTask(Action action, int timeout) : this(action, CancellationToken.None, timeout)
{
}
protected TimeoutTask(Action action, CancellationToken token) : this(action, token, Timeout.Infinite)
{
}
protected TimeoutTask(Action action, CancellationToken token, int timeout = Timeout.Infinite)
{
_action = action;
_tcs = new TaskCompletionSource<AsyncCompletedEventArgs>();
if (timeout != Timeout.Infinite)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
cts.CancelAfter(timeout);
_token = cts.Token;
}
else
{
_token = token;
}
}
#endregion
#region 私有方法
/// <summary>
/// 运行
/// </summary>
/// <returns></returns>
private async Task<AsyncCompletedEventArgs> Run()
{
_asyncCompletedEvent += AsyncCompletedEventHandler;
try
{
using (_token.Register(() => _tcs.TrySetCanceled()))
{
ExecuteAction();
return await _tcs.Task.ConfigureAwait(false);
}
}
finally
{
_asyncCompletedEvent -= AsyncCompletedEventHandler;
}
}
/// <summary>
/// 执行Action
/// </summary>
private void ExecuteAction()
{
Task.Factory.StartNew(() =>
{
_action.Invoke();
OnAsyncCompleteEvent(null);
});
}
/// <summary>
/// 异步完成事件处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AsyncCompletedEventHandler(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
_tcs.TrySetCanceled();
}
else if (e.Error != null)
{
_tcs.TrySetException(e.Error);
}
else
{
_tcs.TrySetResult(e);
}
}
/// <summary>
/// 触发异步完成事件
/// </summary>
/// <param name="userState"></param>
private void OnAsyncCompleteEvent(object userState)
{
if (_asyncCompletedEvent != null)
{
_asyncCompletedEvent(this, new AsyncCompletedEventArgs(error: null, cancelled: false, userState: userState));
}
}
#endregion
}
/// <summary>
/// 有返回值,可超时,可取消的Task
/// </summary>
/// <typeparam name="T"></typeparam>
public class TimeoutTask<T>
{
#region 字段
private Func<T> _func;
private CancellationToken _token;
private event AsyncCompletedEventHandler _asyncCompletedEvent;
private TaskCompletionSource<AsyncCompletedEventArgs> _tcs;
#endregion
#region 静态方法
public static async Task<T> StartNewTask(Func<T> func, CancellationToken token,
int timeout = Timeout.Infinite)
{
var task = new TimeoutTask<T>(func, token, timeout);
return await task.Run();
}
public static async Task<T> StartNewTask(Func<T> func, int timeout)
{
return await TimeoutTask<T>.StartNewTask(func, CancellationToken.None, timeout);
}
public static async Task<T> StartNewTask(Func<T> func, CancellationToken token)
{
return await TimeoutTask<T>.StartNewTask(func, token, Timeout.Infinite);
}
#endregion
#region 构造
protected TimeoutTask(Func<T> func, CancellationToken token) : this(func, token, Timeout.Infinite)
{
}
protected TimeoutTask(Func<T> func, int timeout = Timeout.Infinite) : this(func, CancellationToken.None, timeout)
{
}
protected TimeoutTask(Func<T> func, CancellationToken token, int timeout = Timeout.Infinite)
{
_func = func;
_tcs = new TaskCompletionSource<AsyncCompletedEventArgs>();
if (timeout != Timeout.Infinite)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
cts.CancelAfter(timeout);
_token = cts.Token;
}
else
{
_token = token;
}
}
#endregion
#region 私有方法
/// <summary>
/// 运行Task
/// </summary>
/// <returns></returns>
private async Task<T> Run()
{
_asyncCompletedEvent += AsyncCompletedEventHandler;
try
{
using (_token.Register(() => _tcs.TrySetCanceled()))
{
ExecuteFunc();
var args = await _tcs.Task.ConfigureAwait(false);
return (T)args.UserState;
}
}
finally
{
_asyncCompletedEvent -= AsyncCompletedEventHandler;
}
}
/// <summary>
/// 执行
/// </summary>
private void ExecuteFunc()
{
ThreadPool.QueueUserWorkItem(s =>
{
var result = _func.Invoke();
OnAsyncCompleteEvent(result);
});
}
/// <summary>
/// 异步完成事件处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AsyncCompletedEventHandler(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
_tcs.TrySetCanceled();
}
else if (e.Error != null)
{
_tcs.TrySetException(e.Error);
}
else
{
_tcs.TrySetResult(e);
}
}
/// <summary>
/// 触发异步完成事件
/// </summary>
/// <param name="userState"></param>
private void OnAsyncCompleteEvent(object userState)
{
if (_asyncCompletedEvent != null)
{
_asyncCompletedEvent(this, new AsyncCompletedEventArgs(error: null, cancelled: false, userState: userState));
}
}
#endregion
}
使用起来也很方便
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
try
{
await TimeoutTask.StartNewTask(LongTimeWork, 6000);
var result = await TimeoutTask<string>.StartNewTask(LongTimeWork2, 2000);
textBlock.Text = result;
}
catch (Exception ex)
{
}
}
private void LongTimeWork()
{
Thread.Sleep(5000);
}
private string LongTimeWork2()
{
Thread.Sleep(5000);
return "XXD";
}
其中有一些很少见的CancellationTokenSource CancellationToken TaskCompletionSource AsyncCompletedEventHandler AsyncCompletedEventArgs
不要怕,MSDN上一会就弄懂了。记录一下,算是这两天的研究成果。
async/await Task Timeout的更多相关文章
- async,await,Task 的一些用法
async,await,Task 的一些用法 private void Form1_Load(object sender, EventArgs e) { Display(); } public asy ...
- C#扫盲篇(四):.NET Core 的异步编程-只讲干货(async,await,Task)
关于async,await,task的用法和解释这里就不要说明了,网上一查一大堆.至于为啥还要写这篇文章,主要是其他文章水分太多,不适合新手学习和理解.以下内容纯属个人理解,如果有误,请高手指正.本文 ...
- async await Task
一.使用Task 引用命名空间 using System.Threading.Tasks; 1.工厂方式 Task.Factory.StartNew(() => {Console.WriteLi ...
- async await task.Result 卡死
在如下代码中: public async Task<string> GetData() { return await DoWork(); } 在UI线程中调用 var data = Get ...
- async await Task 使用方法
使用概述 C#的使用过程中,除了以前的Thread.ThreadPool等用来开一个线程用来处理异步的内容.还可以使用新特性来处理异步.比以前的Thread和AutoResetEvent.delege ...
- C#多线程和异步(二)——Task和async/await详解
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- C#多线程和异步(二)——Task和async/await详解(转载)
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- Task和async/await详解
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
- C#多线程和异步——Task和async/await详解
阅读目录 一.什么是异步 二.Task介绍 1 Task创建和运行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延续操作(WhenAny/WhenAll/Cont ...
随机推荐
- Ext.Net MVC 配置(2)
在VS2012中使用NuGet类库管理器配置EXT.NET MVC环境在“Ext.Net MVC 配置(1)”文章已经写到,方法很简单也很方便,但就是不知道它在安装这些库时对项目做了什么很不爽. 通过 ...
- 浅谈HTML5单页面架构(三)—— 回归本真:自定义路由 + requirejs + zepto + underscore
本文转载自:http://www.cnblogs.com/kenkofox/p/4650310.html 不过,这一篇,我想进一步探讨一下这两个框架的优缺点,另外,再进一步,抛开这两个框架,回到本真, ...
- Volley框架设置sessionid
(偷懒,写简略点) 自定义一个Request类 public class MyRequest extends Request<JSONObject> 存储上一次连接的sessionid ...
- IOS开发基础知识--碎片21
1:[UIScreen mainScreen].scale知识点 当屏幕分别为640x940时[[UIScreen mainScreen] scale]=2.0 当屏幕分别为320x480时[[UIS ...
- IOS开发基础知识--碎片30
1:ios 相册操作 ALAssetsLibrary 知识点 a ALAssetsLibrary 实例为我们提供了获取相册(照片app)中的图片和视频的功能.在ios8 photos framewor ...
- iOS 疑难杂症 — — 推送本地国际化 loc-key 本地化失败的问题
声明 欢迎转载,但请保留文章原始出处:) 博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com 正文 一.准备 推送本地国际化官方 ...
- RunTime&RunLoop初见
什么是runtime 1> runtime是一套底层的c语言API(包括很多强大实用的c语言类型,c语言函数); 2>实际上,平时我们编写的oc代码,底层都是基于runtime实现的 也就 ...
- C语言链表实现约瑟夫环问题
需求表达:略 分析: 实现: #include<stdio.h> #include<stdlib.h> typedef struct node { int payload ; ...
- SqlServer--模糊查询-通配符
查询所有姓张的同学Select * from student where left(sName,1)='张' 看上去很美,如果改成查询名字中带亮的学生怎么做?换一种做法 like Select ...
- C# 6.0新特性---语法糖
转载:http://www.cnblogs.com/TianFang/p/3928172.html 所谓语法糖就是在编译器里写做文章,达到简化代码书写的目的,要慎重使用,省略过多不易理解. NULL检 ...