You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步/并行编程中持续运行任务的异常处理与重启方案问询

这类持续运行的异步/并行任务完全可行,而且是IoT监控场景下的常见模式。我来帮你解决异常捕获、自动重启的问题,同时优化你的代码并分享一些最佳实践。

1. 核心解决方案:异常捕获与自动重启的通用包装器

你需要一个通用的方法来包裹任务逻辑,负责处理异常、记录日志,并在任务失败时自动重启。这个包装器可以复用在所有持续运行的任务上:

private static async Task RunContinuousTask(Func<CancellationToken, Task> taskLogic, 
                                            TimeSpan interval, 
                                            CancellationToken cancellationToken,
                                            TimeSpan restartDelay = default)
{
    if (restartDelay == default) restartDelay = TimeSpan.FromSeconds(5); // 默认失败后延迟5秒重启

    while (!cancellationToken.IsCancellationRequested)
    {
        try
        {
            await taskLogic(cancellationToken);
            await Task.Delay(interval, cancellationToken); // 任务正常完成后等待间隔时间
        }
        catch (OperationCanceledException)
        {
            // 任务被主动取消,退出循环
            Console.WriteLine("任务已被取消,退出执行");
            break;
        }
        catch (Exception ex)
        {
            // 记录异常信息
            Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 任务执行失败: {ex.GetType().Name} - {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
            
            // 等待重启延迟,避免频繁重启
            await Task.Delay(restartDelay, cancellationToken);
        }
    }
}

这个方法的作用是:

  • 循环执行传入的任务逻辑
  • 捕获非取消类异常,记录日志后延迟重启
  • 处理取消令牌,收到取消信号后优雅退出

2. 优化你的任务逻辑

现在把你的三个任务改成符合异步规范的逻辑,并使用上面的包装器启动:

优化后的完整代码

using System;
using System.Threading;
using System.Threading.Tasks;

namespace taskTest
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            // 使用一个统一的取消令牌源,方便整体管理所有任务的取消
            using var cts = new CancellationTokenSource();

            // 启动三个持续任务,使用统一的取消令牌
            var task1 = RunContinuousTask(ExecuteSensorPolling, TimeSpan.FromSeconds(1), cts.Token);
            var task2 = RunContinuousTask(ExecuteServerHealthCheck, TimeSpan.FromSeconds(10), cts.Token);
            var task3 = RunContinuousTask(ExecuteSensorListSync, TimeSpan.FromMinutes(1), cts.Token);

            Console.WriteLine("所有任务已启动,按任意键停止...");
            Console.ReadLine();

            // 发送取消信号,停止所有任务
            cts.Cancel();
            await Task.WhenAll(task1, task2, task3);
            Console.WriteLine("所有任务已停止");
        }

        #region 任务逻辑实现
        /// <summary>
        /// 每1秒轮询传感器状态
        /// </summary>
        private static async Task ExecuteSensorPolling(CancellationToken cancellationToken)
        {
            // 这里替换成你的SOAP API调用逻辑
            Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 轮询传感器状态...");
            // 模拟API调用延迟
            await Task.Delay(500, cancellationToken);
        }

        /// <summary>
        /// 每10秒检查Web服务器在线状态
        /// </summary>
        private static async Task ExecuteServerHealthCheck(CancellationToken cancellationToken)
        {
            Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 检查服务器在线状态...");
            // 模拟可能抛出的异常
            if (DateTime.Now.Second % 30 == 0) // 每30秒模拟一次异常
            {
                throw new InvalidOperationException("服务器连接超时");
            }
            await Task.Delay(200, cancellationToken);
        }

        /// <summary>
        /// 每分钟同步传感器列表(新增/删除)
        /// </summary>
        private static async Task ExecuteSensorListSync(CancellationToken cancellationToken)
        {
            Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 同步传感器列表...");
            await Task.Delay(1000, cancellationToken);
        }
        #endregion

        #region 通用持续任务包装器
        private static async Task RunContinuousTask(Func<CancellationToken, Task> taskLogic, 
                                                    TimeSpan interval, 
                                                    CancellationToken cancellationToken,
                                                    TimeSpan restartDelay = default)
        {
            if (restartDelay == default) restartDelay = TimeSpan.FromSeconds(5);

            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    await taskLogic(cancellationToken);
                    await Task.Delay(interval, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 任务已取消");
                    break;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 任务执行失败,将在{restartDelay.TotalSeconds}秒后重启:");
                    Console.WriteLine($"异常类型: {ex.GetType().Name}");
                    Console.WriteLine($"异常消息: {ex.Message}");
                    Console.WriteLine($"堆栈跟踪: {ex.StackTrace}\n");

                    try
                    {
                        await Task.Delay(restartDelay, cancellationToken);
                    }
                    catch (OperationCanceledException)
                    {
                        // 等待重启期间收到取消信号,直接退出
                        break;
                    }
                }
            }
        }
        #endregion
    }
}

3. 原代码中的问题纠正

  • 同步循环导致CPU空转:原Task1中Task.Delay(1000)没有用await,会导致循环瞬间执行,占用大量CPU。改成异步后用await可以让线程在等待期间释放,提升性能。
  • 未处理的异常:原Task2的catch块是空的,无法排查问题;原Task3如果抛出异常会变成未观察到的异常(可能导致进程崩溃),现在统一在包装器中处理。
  • 分散的取消令牌:原代码用了三个独立的CancellationTokenSource,不利于统一管理任务的启动和停止,现在用一个统一的源,所有任务共享同一个令牌。
  • 任务启动方式不规范:原Task1和Task2是同步方法直接调用,没有用Task.Run或异步启动,会阻塞主线程(虽然原代码有Console.ReadLine,但长期运行的程序应该避免同步阻塞)。

4. 最佳实践建议

  • 优先使用异步/await:所有IO密集型操作(比如调用SOAP API、网络请求)都应该用异步方法,避免阻塞线程池线程。
  • 统一取消令牌管理:用一个CancellationTokenSource管理所有相关任务,确保可以优雅地停止所有任务,避免资源泄漏。
  • 完善的异常日志:不要只记录异常消息,还要记录异常类型、堆栈跟踪、发生时间,方便后续排查问题。如果是生产环境,建议使用专业日志框架(如Serilog、NLog)代替Console.WriteLine。
  • 合理的重启策略:异常后不要立即重启,加一个延迟(比如5秒),避免因网络波动等临时问题导致任务频繁重启,消耗资源。
  • 任务幂等性:确保任务逻辑是幂等的,即重复执行不会产生不良影响(比如轮询传感器状态、检查服务器健康都是幂等的)。
  • 避免未观察到的异常:所有Task的异常都必须被处理,要么通过await捕获,要么通过ContinueWith处理,否则在旧版本的.NET中可能导致进程崩溃。

内容的提问来源于stack exchange,提问作者technologyresearcher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 01:42:33