作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
我的某个 thread local 的对象的方法中,抛出了一个异常,且没有捕获。
运行发现程序正常退出,且没有预期中的输出。
如何才能在异常发生时,打印堆栈信息,并且异常退出?
下面是具体做法:
using System;
using System.Threading;
using System.CommandLine;
using System.Threading.Tasks;
internal static class Program {
private const int UnhandledExceptionExitCode = 99;
private static int _hasPrintedUnhandledException;
private static Timer? _unobservedTaskExceptionWatchdog;
static Program()
{
ConfigureGlobalExceptionHandling();
}
public static async Task Main(string[] args) {
// 这里引用 thread local
// 并且在 thread local 对象中抛出未捕获的异常
}
private static void ConfigureGlobalExceptionHandling()
{
AppDomain.CurrentDomain.UnhandledException += (_, eventArgs) =>
{
PrintUnhandledException("AppDomain.CurrentDomain.UnhandledException", eventArgs.ExceptionObject as Exception);
};
TaskScheduler.UnobservedTaskException += (_, eventArgs) =>
{
PrintUnhandledException("TaskScheduler.UnobservedTaskException", eventArgs.Exception);
};
StartUnobservedTaskExceptionWatchdog();
}
private static void StartUnobservedTaskExceptionWatchdog()
{
_unobservedTaskExceptionWatchdog = new Timer(_ =>
{
try
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
catch (Exception ex)
{
PrintUnhandledException("UnobservedTaskExceptionWatchdog", ex);
}
}, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
}
private static void PrintUnhandledException(string source, Exception? exception)
{
if (Interlocked.Exchange(ref _hasPrintedUnhandledException, 1) == 1)
{
return;
}
Console.Error.WriteLine($"[{DateTimeOffset.UtcNow:u}] Unhandled exception caught from {source}");
if (exception is null)
{
Console.Error.WriteLine("Exception object was null.");
}
else
{
Console.Error.WriteLine(exception);
}
Console.Error.Flush();
Environment.Exit(UnhandledExceptionExitCode);
}
}
出现异常后的效果如下:
[2026-02-02 04:47:36Z] Unhandled exception caught from TaskScheduler.UnobservedTaskException
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. (Connection refused (127.0.0.1:9428))
---> System.Net.Http.HttpRequestException: Connection refused (127.0.0.1:9428)
---> System.Net.Sockets.SocketException (111): Connection refused
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.InjectNewHttp11ConnectionAsync(QueueItem queueItem)
at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.SocketsHttpHandler.<SendAsync>g__CreateHandlerAndSendAsync|115_0(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at ConsoleLogger.ThreadLocalLogger.writeJsonline(BufferWrapper wrapper) in /home/ahfu/code/github.com/ahfuzhang/QiWa/src/ConsoleLogger/ThreadLocalLogger.cs:line 106
at ConsoleLogger.ThreadLocalLogger.writeLog(BufferWrapper wrapper) in /home/ahfu/code/github.com/ahfuzhang/QiWa/src/ConsoleLogger/ThreadLocalLogger.cs:line 130
at ConsoleLogger.ThreadLocalLogger.<>c__DisplayClass16_0.<<TimerLoop>b__0>d.MoveNext() in /home/ahfu/code/github.com/ahfuzhang/QiWa/src/ConsoleLogger/ThreadLocalLogger.cs:line 173
--- End of inner exception stack trace ---
make: *** [Makefile:20: run] Error 99

浙公网安备 33010602011771号