18、线程中的异常处理

线程中的异常处理

知识点概述

线程中的异常处理是多线程编程的重要方面,不正确的异常处理可能导致应用程序崩溃或数据损坏。在.NET中,线程异常处理有以下特点:

  • 未处理异常: 线程中未处理的异常会导致应用程序终止
  • 异常传播: 线程异常不会自动传播到调用线程
  • Task异常: Task提供了更好的异常处理机制
  • 聚合异常: 多个任务的异常会被包装在AggregateException中
  • 全局异常处理: 可以设置全局异常处理器

代码案例

案例1:Thread中的异常处理

using System;
using System.Threading;

class ThreadExceptionHandling
{
    static void Main()
    {
        Console.WriteLine("Thread异常处理演示:");
        
        // 设置全局未处理异常事件
        AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
        
        // 创建会抛出异常的线程
        Thread faultyThread = new Thread(FaultyWorker);
        faultyThread.IsBackground = false; // 设置为前台线程
        faultyThread.Start();
        
        // 创建正常工作的线程
        Thread normalThread = new Thread(NormalWorker);
        normalThread.Start();
        
        // 等待线程完成
        try
        {
            normalThread.Join();
            faultyThread.Join();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"主线程捕获异常: {ex.Message}");
        }
        
        Console.WriteLine("主线程结束");
    }
    
    static void FaultyWorker()
    {
        try
        {
            Console.WriteLine("有问题的线程开始工作");
            Thread.Sleep(1000);
            
            // 故意抛出异常
            throw new InvalidOperationException("线程中发生了错误!");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"线程内部捕获异常: {ex.Message}");
            // 如果不在这里处理,异常会导致应用程序终止
        }
    }
    
    static void NormalWorker()
    {
        try
        {
            Console.WriteLine("正常线程开始工作");
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine($"正常工作步骤 {i + 1}");
                Thread.Sleep(500);
            }
            Console.WriteLine("正常线程完成工作");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"正常线程异常: {ex.Message}");
        }
    }
    
    static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine($"全局未处理异常: {e.ExceptionObject}");
        Console.WriteLine($"应用程序即将终止: {e.IsTerminating}");
    }
}

案例2:Task中的异常处理

using System;
using System.Threading.Tasks;

class TaskExceptionHandling
{
    static async Task Main()
    {
        Console.WriteLine("Task异常处理演示:");
        
        // 方式1:使用try-catch处理单个Task异常
        await HandleSingleTaskException();
        
        Console.WriteLine("\n" + new string('=', 50) + "\n");
        
        // 方式2:处理多个Task的异常
        await HandleMultipleTaskExceptions();
        
        Console.WriteLine("\n" + new string('=', 50) + "\n");
        
        // 方式3:使用ContinueWith处理异常
        await HandleExceptionWithContinueWith();
    }
    
    static async Task HandleSingleTaskException()
    {
        Console.WriteLine("1. 单个Task异常处理:");
        
        try
        {
            await Task.Run(() =>
            {
                Console.WriteLine("Task开始执行");
                Thread.Sleep(1000);
                throw new ArgumentException("Task中发生了参数错误!");
            });
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine($"捕获到ArgumentException: {ex.Message}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"捕获到其他异常: {ex.Message}");
        }
    }
    
    static async Task HandleMultipleTaskExceptions()
    {
        Console.WriteLine("2. 多个Task异常处理:");
        
        Task task1 = Task.Run(() =>
        {
            Thread.Sleep(500);
            throw new InvalidOperationException("任务1异常");
        });
        
        Task task2 = Task.Run(() =>
        {
            Thread.Sleep(800);
            throw new ArgumentNullException("任务2异常");
        });
        
        Task task3 = Task.Run(() =>
        {
            Thread.Sleep(300);
            Console.WriteLine("任务3正常完成");
        });
        
        try
        {
            await Task.WhenAll(task1, task2, task3);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"第一个异常: {ex.Message}");
            
            // 检查所有任务的异常
            CheckAllTaskExceptions(task1, task2, task3);
        }
    }
    
    static void CheckAllTaskExceptions(params Task[] tasks)
    {
        Console.WriteLine("检查所有任务状态:");
        
        foreach (var task in tasks)
        {
            if (task.IsFaulted && task.Exception != null)
            {
                foreach (var innerEx in task.Exception.InnerExceptions)
                {
                    Console.WriteLine($"  - 任务异常: {innerEx.GetType().Name}: {innerEx.Message}");
                }
            }
            else if (task.IsCompletedSuccessfully)
            {
                Console.WriteLine("  - 任务正常完成");
            }
        }
    }
    
    static async Task HandleExceptionWithContinueWith()
    {
        Console.WriteLine("3. 使用ContinueWith处理异常:");
        
        Task<int> faultyTask = Task.Run(() =>
        {
            Thread.Sleep(1000);
            throw new DivideByZeroException("除零错误!");
            return 42; // 不会执行到这里
        });
        
        Task continuationTask = faultyTask.ContinueWith(task =>
        {
            if (task.IsFaulted)
            {
                Console.WriteLine($"任务失败: {task.Exception?.GetBaseException().Message}");
                return -1;
            }
            else if (task.IsCompletedSuccessfully)
            {
                Console.WriteLine($"任务成功,结果: {task.Result}");
                return task.Result;
            }
            else
            {
                Console.WriteLine("任务被取消");
                return 0;
            }
        });
        
        await continuationTask;
    }
}

案例3:聚合异常处理

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

class AggregateExceptionHandling
{
    static async Task Main()
    {
        Console.WriteLine("聚合异常处理演示:");
        
        // 创建多个会抛出不同异常的任务
        Task[] tasks = new Task[]
        {
            CreateTaskWithException<ArgumentException>("参数错误", 500),
            CreateTaskWithException<InvalidOperationException>("操作无效", 800),
            CreateTaskWithException<NotSupportedException>("不支持的操作", 300),
            CreateNormalTask("正常任务", 1000),
            CreateTaskWithException<DivideByZeroException>("除零错误", 600)
        };
        
        try
        {
            await Task.WhenAll(tasks);
            Console.WriteLine("所有任务正常完成");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"捕获的第一个异常: {ex.GetType().Name}: {ex.Message}");
            
            // 处理聚合异常
            await HandleAggregateExceptions(tasks);
        }
    }
    
    static Task CreateTaskWithException<T>(string message, int delay) where T : Exception, new()
    {
        return Task.Run(async () =>
        {
            await Task.Delay(delay);
            Console.WriteLine($"任务抛出 {typeof(T).Name}");
            
            // 使用反射创建异常实例
            var exception = (T)Activator.CreateInstance(typeof(T), message);
            throw exception;
        });
    }
    
    static Task CreateNormalTask(string name, int delay)
    {
        return Task.Run(async () =>
        {
            await Task.Delay(delay);
            Console.WriteLine($"{name} 正常完成");
        });
    }
    
    static async Task HandleAggregateExceptions(Task[] tasks)
    {
        Console.WriteLine("\n详细异常分析:");
        
        // 等待所有任务完成(包括失败的)
        await Task.WhenAll(tasks.Select(async task =>
        {
            try
            {
                await task;
            }
            catch
            {
                // 忽略异常,我们只是想等待所有任务完成
            }
        }));
        
        // 分析各种异常类型
        var faultedTasks = tasks.Where(t => t.IsFaulted).ToArray();
        
        if (faultedTasks.Any())
        {
            Console.WriteLine($"失败任务数量: {faultedTasks.Length}");
            
            // 按异常类型分组
            var exceptionGroups = faultedTasks
                .SelectMany(t => t.Exception.InnerExceptions)
                .GroupBy(ex => ex.GetType())
                .ToArray();
            
            foreach (var group in exceptionGroups)
            {
                Console.WriteLine($"\n{group.Key.Name} ({group.Count()} 个):");
                foreach (var exception in group)
                {
                    Console.WriteLine($"  - {exception.Message}");
                }
            }
        }
        
        // 统计任务状态
        var completedCount = tasks.Count(t => t.IsCompletedSuccessfully);
        var faultedCount = tasks.Count(t => t.IsFaulted);
        var canceledCount = tasks.Count(t => t.IsCanceled);
        
        Console.WriteLine($"\n任务执行统计:");
        Console.WriteLine($"成功: {completedCount}, 失败: {faultedCount}, 取消: {canceledCount}");
    }
}

案例4:异常重试机制

using System;
using System.Threading.Tasks;

class ExceptionRetryMechanism
{
    static async Task Main()
    {
        Console.WriteLine("异常重试机制演示:");
        
        // 测试重试机制
        try
        {
            var result = await ExecuteWithRetry(
                () => UnreliableOperation(),
                maxRetries: 3,
                delay: TimeSpan.FromSeconds(1)
            );
            
            Console.WriteLine($"操作成功,结果: {result}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"重试失败,最终异常: {ex.Message}");
        }
        
        Console.WriteLine("\n" + new string('=', 50) + "\n");
        
        // 测试带条件的重试
        await TestConditionalRetry();
    }
    
    static async Task<T> ExecuteWithRetry<T>(
        Func<Task<T>> operation,
        int maxRetries,
        TimeSpan delay)
    {
        Exception lastException = null;
        
        for (int attempt = 1; attempt <= maxRetries + 1; attempt++)
        {
            try
            {
                Console.WriteLine($"尝试执行操作 (第 {attempt} 次)");
                return await operation();
            }
            catch (Exception ex)
            {
                lastException = ex;
                Console.WriteLine($"第 {attempt} 次尝试失败: {ex.Message}");
                
                if (attempt <= maxRetries)
                {
                    Console.WriteLine($"等待 {delay.TotalSeconds} 秒后重试...");
                    await Task.Delay(delay);
                }
            }
        }
        
        throw new InvalidOperationException(
            $"操作在 {maxRetries + 1} 次尝试后仍然失败", lastException);
    }
    
    static async Task<string> UnreliableOperation()
    {
        await Task.Delay(500);
        
        // 模拟不可靠的操作,70%概率失败
        if (new Random().NextDouble() < 0.7)
        {
            throw new InvalidOperationException("模拟的网络错误");
        }
        
        return "操作成功完成";
    }
    
    static async Task TestConditionalRetry()
    {
        Console.WriteLine("条件重试演示:");
        
        try
        {
            await ExecuteWithConditionalRetry();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"条件重试最终失败: {ex.Message}");
        }
    }
    
    static async Task ExecuteWithConditionalRetry()
    {
        int maxRetries = 3;
        int attempt = 0;
        
        while (attempt <= maxRetries)
        {
            attempt++;
            
            try
            {
                await SimulateOperationWithDifferentErrors(attempt);
                Console.WriteLine("操作成功完成");
                return;
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine($"参数错误不重试: {ex.Message}");
                throw; // 参数错误不应该重试
            }
            catch (InvalidOperationException ex) when (attempt <= maxRetries)
            {
                Console.WriteLine($"临时错误,准备重试: {ex.Message}");
                await Task.Delay(1000);
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine($"重试次数已用尽: {ex.Message}");
                throw;
            }
        }
    }
    
    static async Task SimulateOperationWithDifferentErrors(int attempt)
    {
        await Task.Delay(300);
        
        switch (attempt)
        {
            case 1:
                throw new InvalidOperationException("网络超时 - 可重试");
            case 2:
                throw new InvalidOperationException("服务暂时不可用 - 可重试");
            case 3:
                throw new ArgumentException("无效的请求参数 - 不应重试");
            default:
                Console.WriteLine("操作执行成功");
                break;
        }
    }
}

案例5:异常日志和监控

using System;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.IO;

class ExceptionLoggingAndMonitoring
{
    private static readonly ConcurrentQueue<ExceptionInfo> _exceptionLog = new ConcurrentQueue<ExceptionInfo>();
    
    static async Task Main()
    {
        Console.WriteLine("异常日志和监控演示:");
        
        // 启动异常监控
        Task monitoringTask = StartExceptionMonitoring();
        
        // 模拟多个任务运行
        Task[] workTasks = new Task[]
        {
            RunTaskWithLogging("任务1", () => throw new ArgumentException("参数错误")),
            RunTaskWithLogging("任务2", () => Thread.Sleep(1000)),
            RunTaskWithLogging("任务3", () => throw new InvalidOperationException("操作错误")),
            RunTaskWithLogging("任务4", () => Thread.Sleep(500)),
            RunTaskWithLogging("任务5", () => throw new DivideByZeroException("除零错误"))
        };
        
        // 等待所有工作任务完成
        await Task.WhenAll(workTasks.Select(async task =>
        {
            try
            {
                await task;
            }
            catch
            {
                // 异常已经被记录
            }
        }));
        
        // 等待一下让监控输出完整信息
        await Task.Delay(2000);
        
        // 生成异常报告
        await GenerateExceptionReport();
        
        Console.WriteLine("演示完成");
    }
    
    static async Task<T> RunTaskWithLogging<T>(string taskName, Func<T> operation)
    {
        try
        {
            Console.WriteLine($"{taskName} 开始执行");
            var result = await Task.Run(operation);
            Console.WriteLine($"{taskName} 成功完成");
            return result;
        }
        catch (Exception ex)
        {
            // 记录异常
            LogException(taskName, ex);
            Console.WriteLine($"{taskName} 执行失败: {ex.Message}");
            throw;
        }
    }
    
    static void LogException(string context, Exception exception)
    {
        var exceptionInfo = new ExceptionInfo
        {
            Context = context,
            Exception = exception,
            Timestamp = DateTime.Now,
            ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId
        };
        
        _exceptionLog.Enqueue(exceptionInfo);
    }
    
    static Task StartExceptionMonitoring()
    {
        return Task.Run(async () =>
        {
            Console.WriteLine("异常监控已启动...\n");
            
            int lastCount = 0;
            while (true)
            {
                int currentCount = _exceptionLog.Count;
                if (currentCount > lastCount)
                {
                    Console.WriteLine($"[监控] 检测到 {currentCount - lastCount} 个新异常");
                    lastCount = currentCount;
                }
                
                await Task.Delay(1000);
                
                // 简单的退出条件
                if (currentCount > 0 && DateTime.Now.Second % 10 == 0)
                {
                    break;
                }
            }
        });
    }
    
    static async Task GenerateExceptionReport()
    {
        Console.WriteLine("\n生成异常报告:");
        Console.WriteLine("================");
        
        var exceptions = _exceptionLog.ToArray();
        
        if (!exceptions.Any())
        {
            Console.WriteLine("没有记录到异常");
            return;
        }
        
        // 按异常类型分组统计
        var exceptionStats = exceptions
            .GroupBy(e => e.Exception.GetType().Name)
            .Select(g => new { Type = g.Key, Count = g.Count() })
            .OrderByDescending(s => s.Count);
        
        Console.WriteLine("异常类型统计:");
        foreach (var stat in exceptionStats)
        {
            Console.WriteLine($"  {stat.Type}: {stat.Count} 次");
        }
        
        Console.WriteLine("\n详细异常列表:");
        foreach (var info in exceptions.OrderBy(e => e.Timestamp))
        {
            Console.WriteLine($"[{info.Timestamp:HH:mm:ss.fff}] {info.Context} - " +
                            $"{info.Exception.GetType().Name}: {info.Exception.Message} " +
                            $"(线程ID: {info.ThreadId})");
        }
        
        // 保存到文件
        await SaveExceptionReportToFile(exceptions);
    }
    
    static async Task SaveExceptionReportToFile(ExceptionInfo[] exceptions)
    {
        string fileName = $"exception_report_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
        
        using (StreamWriter writer = new StreamWriter(fileName))
        {
            await writer.WriteLineAsync($"异常报告 - 生成时间: {DateTime.Now}");
            await writer.WriteLineAsync(new string('=', 50));
            
            foreach (var info in exceptions)
            {
                await writer.WriteLineAsync($"时间: {info.Timestamp}");
                await writer.WriteLineAsync($"上下文: {info.Context}");
                await writer.WriteLineAsync($"异常类型: {info.Exception.GetType().FullName}");
                await writer.WriteLineAsync($"异常消息: {info.Exception.Message}");
                await writer.WriteLineAsync($"线程ID: {info.ThreadId}");
                await writer.WriteLineAsync($"堆栈跟踪:\n{info.Exception.StackTrace}");
                await writer.WriteLineAsync(new string('-', 30));
            }
        }
        
        Console.WriteLine($"\n异常报告已保存到: {fileName}");
    }
    
    class ExceptionInfo
    {
        public string Context { get; set; }
        public Exception Exception { get; set; }
        public DateTime Timestamp { get; set; }
        public int ThreadId { get; set; }
    }
}

知识点总结

  1. 异常隔离: 线程中的异常不会自动传播,需要显式处理
  2. 全局处理: 设置AppDomain.UnhandledException处理未捕获的异常
  3. Task优势: Task提供了比Thread更好的异常处理机制
  4. 聚合异常: 多任务场景下使用AggregateException处理多个异常
  5. 重试机制: 对于临时性错误,可以实现自动重试机制
  6. 异常分类: 区分可重试和不可重试的异常类型
  7. 监控日志: 建立完善的异常记录和监控机制有助于问题诊断
posted @ 2025-08-27 01:24  jessqiu  阅读(34)  评论(0)    收藏  举报