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的更多相关文章

  1. async,await,Task 的一些用法

    async,await,Task 的一些用法 private void Form1_Load(object sender, EventArgs e) { Display(); } public asy ...

  2. C#扫盲篇(四):.NET Core 的异步编程-只讲干货(async,await,Task)

    关于async,await,task的用法和解释这里就不要说明了,网上一查一大堆.至于为啥还要写这篇文章,主要是其他文章水分太多,不适合新手学习和理解.以下内容纯属个人理解,如果有误,请高手指正.本文 ...

  3. async await Task

    一.使用Task 引用命名空间 using System.Threading.Tasks; 1.工厂方式 Task.Factory.StartNew(() => {Console.WriteLi ...

  4. async await task.Result 卡死

    在如下代码中: public async Task<string> GetData() { return await DoWork(); } 在UI线程中调用 var data = Get ...

  5. async await Task 使用方法

    使用概述 C#的使用过程中,除了以前的Thread.ThreadPool等用来开一个线程用来处理异步的内容.还可以使用新特性来处理异步.比以前的Thread和AutoResetEvent.delege ...

  6. C#多线程和异步(二)——Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  7. C#多线程和异步(二)——Task和async/await详解(转载)

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  8. Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

  9. C#多线程和异步——Task和async/await详解

    阅读目录 一.什么是异步 二.Task介绍 1 Task创建和运行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延续操作(WhenAny/WhenAll/Cont ...

随机推荐

  1. MySQL基础(非常全)

    MySQL基础 一.MySQL概述 1.什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access ...

  2. 那些过目不忘的H5页面

    原文链接:http://isux.tencent.com/great-mobile-h5-pages.html 从引爆朋友圈的H5小游戏<围住神经猫>,到颠覆传统广告的大众点评H5专题页& ...

  3. 关于C语言函数调用压栈和返回值问题的疑惑

    按照C编译器的约定调用函数时压栈的顺序是从右向左,并且返回值是保存在eax寄存器当中.这个命题本该是成立的,下面用一个小程序来反汇编观察执行过程: #include<stdio.h> in ...

  4. Android 自定义线程池的实战

    前言:在上一篇文章中我们讲到了AsyncTask的基本使用.AsyncTask的封装.AsyncTask 的串行/并行线程队列.自定义线程池.线程池的快速创建方式. 对线程池不了解的同学可以先看 An ...

  5. 二进制配置文件为什么比json等配置文件效率高

    二进制配置文件为什么比json等配置文件高效 项目中用spine做动画,spine可以导出json和二进制的动画配置文件,蛋疼的是spine官方竟然没有提供c的二进制配置解析代码,更没有提供它二进制文 ...

  6. iOS真机测试碰到错误linker command failed with exit code 1 (use -v to see invocation)

    在模拟器上运行正常,但是在模拟器上就会报错,这是因为xocde7之后增加了一个bitcode,bitcode是被编译程序的一种中间形式的代码.包含bitcode配置的程序将会在App store上被编 ...

  7. Windows on Device 项目实践 1 - PWM调光灯制作

    在前一篇文章<Wintel物联网平台-Windows IoT新手入门指南>中,我们讲解了Windows on Device硬件准备和软件开发环境的搭建,以及Hello Blinky项目的演 ...

  8. 微软CodeDom模型学习笔记(全)

    CodeDomProvider MSDN描述 CodeDomProvider可用于创建和检索代码生成器和代码编译器的实例.代码生成器可用于以特定的语言生成代码,而代码编译器可用于将代码编译为程序集. ...

  9. php CGI、Fastcgi、PHP-FPM的详细介绍与之间的关系

    以下PHP CGI.Fastcgi.PHP-FPM的一些信息归纳和汇总----->详细介绍与之间的关系 一:CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的 web ...

  10. java数组对象的浅层复制与深层复制

    实际上,java中数组对象的浅层复制只是复制了对象的引用(参考),而深层复制的才是对象所代表的值.