Cinchoo - 可中止的长时间运行异步任务
提示:
引言
在 .NET 中,有很多方法可以使用 Thread / Task / ThreadPool 启动异步长时间运行的任务。 每种方法都有其优点和缺点。 请参阅 MSDN 以获取更多信息。 使用 TPL,您可以接触到取消模型,从而可以正常停止长时间运行的任务。 在本文中,我将详细介绍一种使用 Cinchoo 框架异步启动可中止的长时间运行任务的方法,并讨论以强制方式中止它的方法。
首先,每个 Windows 应用程序都有一个主线程,用于呈现窗口/执行主任务、处理事件和执行自定义代码。 在单线程环境中,任何长时间运行的任务都会阻塞主线程。 特别是,它会阻塞主 UI 线程,并且应用程序在 Windows 应用程序中变得无响应。
Cinchoo 简化了异步执行长时间运行的任务的模型,并让调用者可以随时中止它。 除了标准行为外,它还提供了一些有用的功能,例如在失败的情况下重试次数,以及通过 API 设置的超时时间。 好了,让我们直接看看如何使用代码来使用它们。
以下是功能摘要
- 随时通过
Abort()
方法中止任务 - 可以指定运行任务的超时时间
- 在任务失败的情况下自动重试
- 通过回调机制决定是否继续重试
在此处下载最新的 Cinchoo 二进制文件 here。 (Nuget 命令:Install-Package Cinchoo)
Using the Code
该 API 通过 ChoActionEx
类公开。 API 的签名如下所示
public static class ChoActionEx
{
public static ChoAbortableAsyncResult RunAsync
(this 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.");
}
}
示例 #5
以下示例显示如何异步执行方法,并在回调中取消它。
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.");
}
}