异步执行可取消的长时间运行任务

在Windows应用程序中,主线程负责渲染窗口、执行主任务、处理事件和执行自定义代码。在单线程环境中,任何长时间运行的任务都会阻塞主线程。特别是,它将阻塞主UI线程,导致Windows应用程序变得无响应。Cinchoo框架简化了异步执行长时间运行任务的模型,并允许调用者随时中止它。除了标准行为外,它还提供了一些有用的功能,如在失败时的重试次数、通过API设置的超时周期等。

本文将详细说明如何使用Cinchoo框架异步启动可取消的长时间运行任务,并讨论如何以强制方式中止它。

Cinchoo框架的功能摘要

通过以下方式随时中止任务:

  • 使用Abort()方法
  • 可以指定任务的超时周期
  • 在任务失败时自动重试
  • 通过回调机制决定是否继续重试

下载最新的Cinchoo二进制文件:

Nuget命令:Install-Package Cinchoo

使用代码

API通过ChoActionEx类公开。API的签名如下:

public static class ChoActionEx { public static ChoAbortableAsyncResult RunAsync( Action action, ChoAbortableAsyncCallback callback = null, object state = null, int timeout = -1, int maxNoOfRetry = 0, int sleepBetweenRetry = 5000); }

其中:

  • action - 长时间运行的任务方法
  • callback - 当相应的异步操作完成时调用的方法
  • state - 定义或包含异步操作信息的用户定义对象
  • timeout - 超时值(毫秒)。-1表示无限
  • maxNoOfRetry - 在失败时进行的最大重试次数
  • sleepBetweenRetry - 重试之间的睡眠时间(毫秒)。默认为5000毫秒

此API方法返回ChoAbortableAsyncResult对象。它封装了对委托的异步操作的结果,并公开了以下成员:

  • AsyncState - 获取RunAsync方法调用中的state参数提供的对象
  • AsyncWaitHandle - 获取封装Win32同步句柄的WaitHandle,允许实现各种同步方案
  • CompletedSynchronously - 获取一个值,指示RunAsync调用是否同步完成
  • IsAborted - 获取一个值,指示服务器是否已中止调用
  • IsCompleted - 获取一个值,指示服务器是否已完成调用
  • IsRetryAttempt - 获取一个值,指示服务器是否已重试调用
  • IsTimeout - 获取一个值,指示方法是否已超时完成
  • Result - 获取async调用的结果值(如果有)
  • RetryCount - 获取服务器进行的重试次数
  • Exception - 获取任务执行过程中捕获的任何异常
  • CanContinue - 获取或设置是否继续重试
  • EndInvoke() - 检索异步操作的返回值。如果异步操作尚未完成,此函数将阻塞直到结果可用
  • Abort() - 中止当前执行的异步调用

示例 #1

以下示例展示了如何异步执行方法,并等待其完成。

class Program { static void Main(string[] args) { Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); // Run method async, wait for it to complete Console.WriteLine("TEST #1: Run method, wait for it to complete..."); ChoAbortableAsyncResult r = ChoActionEx.RunAsync(LongRunningTask); Console.WriteLine("Waiting for worker thread to complete."); r.EndInvoke(); Console.WriteLine(); } private static void LongRunningTask() { Console.WriteLine("Starting task... (Sleeping for 10 secs)"); Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10 * 1000); Console.WriteLine("Task completed."); } }

示例 #2

以下示例展示了如何异步执行方法,并在5秒后中止它。

class Program { static void Main(string[] args) { Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); // Run method async, abort it after 5 secs Console.WriteLine("TEST #2: Run method, abort after 5 secs..."); ChoAbortableAsyncResult r1 = ChoActionEx.RunAsync(LongRunningTask); Console.WriteLine("Waiting for 5 secs..."); Thread.Sleep(5000); Console.WriteLine("Aborting working thread."); r1.Abort(); Console.WriteLine(); } private static void LongRunningTask() { Console.WriteLine("Starting task... (Sleeping for 10 secs)"); Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10 * 1000); Console.WriteLine("Task completed."); } }

示例 #3

以下示例展示了如何异步执行方法,并在5秒后超时。

class Program { static void Main(string[] args) { Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); // Run method async, with timeout 5 secs Console.WriteLine("TEST #3: Run method with 5 secs timeout..."); ChoAbortableAsyncResult r2 = ChoActionEx.RunAsync(LongRunningTask, null, null, 5000); Console.WriteLine("Waiting for worker thread to complete or timeout."); try { r2.EndInvoke(); } catch (Exception e) { Console.WriteLine(e.Message); } Console.WriteLine(); } private static void LongRunningTask() { Console.WriteLine("Starting task... (Sleeping for 10 secs)"); Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10 * 1000); Console.WriteLine("Task completed."); } }

示例 #4

class Program { static void Main(string[] args) { Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); // Run a exception thrown method async Console.WriteLine("TEST #4: Run method with 2 retries..."); ChoAbortableAsyncResult r3 = ChoActionEx.RunAsync(LongRunningTaskWithException, null, null, -1, 2, 5000); Console.WriteLine("Waiting for worker thread to complete."); try { r3.EndInvoke(); } catch (Exception e) { Console.WriteLine(e.Message); } Console.WriteLine(); } private static void LongRunningTaskWithException() { Console.WriteLine("Starting task... (Sleeping for 10 secs)"); Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10 * 1000); throw new ApplicationException("Test task exception."); } } class Program { static void Main(string[] args) { Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); Console.WriteLine("TEST #5: Run method with 2 retries, but cancel the retry on callback..."); ChoAbortableAsyncResult r5 = ChoActionEx.RunAsync(LongRunningTaskWithException, (t) => { ChoAbortableAsyncResult t1 = t as ChoAbortableAsyncResult; Console.WriteLine("Canceling the task..."); if (t1.Exception != null) t1.CanContinue = false; }, null, -1, 2, 5000); try { r5.EndInvoke(); } catch (Exception e) { Console.WriteLine(e.Message); } Console.WriteLine(); } private static void LongRunningTaskWithException() { Console.WriteLine("Starting task... (Sleeping for 10 secs)"); Console.WriteLine("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10 * 1000); throw new ApplicationException("Test task exception."); } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485