C# 多次抛出同一异常实例
面对生成一个异常实例然后可能多次抛出它的代码C# 多次抛出同一异常实例,c#,exception-handling,C#,Exception Handling,面对生成一个异常实例然后可能多次抛出它的代码 private readonly Exception exceptionInstance = new Exception("message"); 多次抛出同一异常实例是否正确?否。不建议从代码中的多个位置抛出同一异常实例(不包括重试!) 异常包含的不仅仅是它的消息,它还包含用于调试的有价值的信息,例如堆栈跟踪。 (Update:现在刚刚测试过。堆栈跟踪可能添加到throw语句中的异常中,因此它与此答案无关)和TargetSite(根据我的测试,它似
private readonly Exception exceptionInstance = new Exception("message");
多次抛出同一异常实例是否正确?否。不建议从代码中的多个位置抛出同一异常实例(不包括重试!) 异常包含的不仅仅是它的消息,它还包含用于调试的有价值的信息,例如堆栈跟踪。 (Update:现在刚刚测试过。堆栈跟踪可能添加到throw语句中的异常中,因此它与此答案无关)和TargetSite(根据我的测试,它似乎是在第一次抛出异常时填充的,但之后再也不会填充)
在代码中的不同位置抛出相同的
异常
实例会使您无法使用某些数据。由于各种已经说明的原因,这是一种不好的做法,但在多线程代码中失败尤其严重,因为异常
类(显然)不是线程安全的,这不是一成不变的
考虑以下代码:
class Program {
static readonly Exception _test = new Exception("test");
static void Main(string[] args) {
ThreadPool.SetMinThreads(10, 8);
var random = new Random();
int num1 = 0;
int num2 = 0;
var tasks = new List<Task>();
for (int i = 0; i < 10; i++) {
tasks.Add(Task.Run(() => {
try {
if (random.Next(0, 2) == 0) {
Interlocked.Increment(ref num1);
Throw1();
}
else {
Interlocked.Increment(ref num2);
Throw2();
}
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("num1: " + num1);
Console.WriteLine("num2: " + num2);
Console.ReadKey();
}
static void Throw1() {
throw _test;
}
static void Throw2() {
throw _test;
}
}
因此,虽然调用了6次Throw1()
,调用了4次Throw2()
,但我们打印的所有10个堆栈跟踪都引用了Throw1()
方法
所以永远不要这样做,因为绝对没有理由这样做。出于好奇,为什么只想使用特定实例?你的意思是
从代码中抛出exceptionInstance
?异常通常只是一个数据对象。你可以一次又一次地使用同一个实例,但我看不出有任何理由这样做,因为它总是包含完全相同的消息,因此不会有非常通用的用法。Singleton?“但为什么呢?”克劳科德,我不知道作者为什么要这么做。我的问题是关于这段代码可能存在的问题。你当然应该重构这段代码。例如,我很确定这不是线程安全的,所以当你从多个线程抛出这个异常时——谁知道会发生什么呢。堆栈跟踪实际上是不同的。如果您从不同的方法抛出相同的异常,您将在catch
块中看到堆栈跟踪(以及TargetSite等属性)每次都会更改。但我并不是说这是一种不好的做法…@Evk是的,现在刚刚测试过。堆栈跟踪可能已添加到throw语句中的异常ReadOnly字段不可变。static保证在被调用之前被初始化。只是随便哪个线程初始化静态wins。在那之后,状态永远不会改变。@P.Brian.Mackey我不同意,因为如果您尝试从我的示例中依次调用Throw1()
和Throw2()
,catch
将打印两个不同的堆栈跟踪,即使异常相同。异常有许多可变字段,一个例子是Message
本身,但还有更多。@P.Brian.Mackey注意,问题不在于重新引用捕获到的异常,当然这很好。问题是在一个字段中存储一个异常实例,然后从多个完全不相关的位置抛出它,而不是仅仅“抛出新异常(“测试”)。这是错误的。@P.Brian.Mackey字段初始化在静态构造函数中已经存在。静态字段初始值设定项的行为方式相同。无论如何,问题不在于如何创建Exception
实例。它只在一个线程上创建一次。问题是它稍后会被多个线程修改。例如,执行Console.WriteLine(ex)
时,这与Console.WriteLine(ex.ToString())
相同<异常的code>ToString()调用各种方法,这些方法修改异常的私有字段。这不是线程安全的。@P.Brian.Mackey例如,ToString()
调用GetClassName
private方法,该方法大致如下:if(\u className==null)\u className=GetClassNameIntl();返回_className
。这样,对ToString()
的无辜调用将修改Exception
的私有字段。在本例中,我们从多个线程执行此操作,这将导致观察到的行为。所有这些都是因为Exception
不是线程安全的,也不应该是线程安全的,因为您不应该这样使用它。
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
System.Exception: test
в ConsoleApp8.Program.Throw1() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 52
в ConsoleApp8.Program.<>c__DisplayClass1_0.<Main>b__0() в H:\VSProjects\SoHelp\ConsoleApp8\Program.cs:строка 32
num1: 6
num2: 4