在平常的电脑使用过程当中,估计最难以忍受的就是软件界面“卡住”“无响应”,在我有限的开发生涯中一直都是在挑战
它。在WPF中,主线程即UI线程,当咱们在UI线程中执行一个很耗时的操做,以致于UI线程没能继续绘制窗体,这时给人
的感受就是“卡住”了。程序员
很耗时
的操做分为2种异步
复杂计算async
I/O操做this
为了有一个良好的用户操做体验,咱们都会使用异步方法,在另一个线程中处理耗时的操做,当操做结束时,仅仅使用
UI线程更新结果到界面。.Net中的异步模型也有不少种,园子里有不少,不过用起来很舒服的仍是async/await。线程
async/await 的引入让咱们编写异步方法更加容易,它的目的就是使得咱们像同步方法同样编写异步方法。上面铺垫稍微
啰嗦了点。立刻进入正题,当咱们在await一个方法时,若是这个方法它是支持超时的,那么当超时时是以异常
的形式来
通知咱们的,这样await如下的方法就没有办法执行了。code
注意:这里补充下,一个Task超时了,并不意味着这个Task就结束了,它仍是会运行,直到结束或是发生异常,一个超 时的Task返回的结果不该该被继续使用,应该丢弃
token
提供了超时设置还好,可是若是这个方法没有超时设置,那岂不就是一直在这里傻等?那确定不,只有本身实现超时,一个
线程是没有办法作超时功能的。通常都是一个线程执行耗时操做,一个线程来计算超时,而且超时了要以异常的形式通知出来,因此
代码应该是这样的:事件
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 }
使用起来也很方便get
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上一会就弄懂了。记录一下,算是这两天的研究成果。