65.9K
CodeProject 正在变化。 阅读更多。
Home

Cinchoo - 可中止的长时间运行异步任务

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2016年5月30日

CPOL

3分钟阅读

viewsIcon

18915

downloadIcon

127

提示: 使用 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.");
    }
}
© . All rights reserved.