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 ...
随机推荐
- iOS 真机测试 App installation failed
真机测试的过程中,出现这种Bug This application's application-identifier entitlement does not match that of the in ...
- VS2012 单元测试之泛型类(Generics Unit Test)
关于单元测试,如果不会用可以参照我的上篇博文————在Visual Studio 2012使用单元测试 首先分享一篇博文,[Visual Studio] 开启Visual Studio 2012通过右 ...
- Android编码规范03
一.整个项目的目录规范化sundy老师建议有:系统目录规范.源代码目录规范. 1.系统目录规范: 指项目目录中不仅包括源代码,还需要包括:需求相关文档.设计文档.计划日志文档.测试文档.项目进行中学习 ...
- 手机APP创建桌面快捷方式
预览: 需要权限: <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT& ...
- RESTORE detected an error on page (0:0) in database
在测试服务器还原生产服务器的一个数据库时遇到了下面错误: System.Data.SqlClient.SqlError: RESTORE detected an error on page (0:0) ...
- 使用 Eclipse 玩转 C、C++
因为做Java开发,所以习惯了使用Eclipse.现在需要写C++程序,真心不想用VS那样的重量级的IDE,VC++6.0又是那么的不友好.使用一款自己熟悉的IDE,工作起来就顺手.为了可以在Ecli ...
- 配置apache的虚拟机+软件下载
第一步: 打开c:/wamp/apache/conf中的httpd.conf文件, 在httpd.conf中ctrl+f输入vhosts 找到那一行将前面的#号去掉 操作如图所示 第二步: 打开虚拟主 ...
- Android Studio failed to open by giving error “Files Locked” 解决方案
windows 7 下的解决方案 导航至 android-studio 安装目录. (默认为C:\Program Files (x86)\Android\android-studio). 往上一层文件 ...
- maven archetype生成自定义项目原型(模板)
maven archetype可以将一个项目做成项目原型,之后只需要以此原型来创建项目,那么初始创建的项目便具有原型项目中的一切配置和代码.通俗讲就是一个项目模板. eclipse中那些快速生成mav ...
- python 数据处理学习pandas之DataFrame
请原谅没有一次写完,本文是自己学习过程中的记录,完善pandas的学习知识,对于现有网上资料的缺少和利用python进行数据分析这本书部分知识的过时,只好以记录的形势来写这篇文章.最如果后续工作定下来 ...