在Windows应用程序中,主线程负责渲染窗口、执行主任务、处理事件和执行自定义代码。在单线程环境中,任何长时间运行的任务都会阻塞主线程。特别是,它将阻塞主UI线程,导致Windows应用程序变得无响应。Cinchoo框架简化了异步执行长时间运行任务的模型,并允许调用者随时中止它。除了标准行为外,它还提供了一些有用的功能,如在失败时的重试次数、通过API设置的超时周期等。
本文将详细说明如何使用Cinchoo框架异步启动可取消的长时间运行任务,并讨论如何以强制方式中止它。
通过以下方式随时中止任务:
下载最新的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()
- 中止当前执行的异步调用以下示例展示了如何异步执行方法,并等待其完成。
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.");
}
}
以下示例展示了如何异步执行方法,并在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.");
}
}
以下示例展示了如何异步执行方法,并在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.");
}
}
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.");
}
}