C# Enum.TryParse-它是线程安全的吗?
我试图弄清楚.NET4.0的Enum.TryParse是否是线程安全的 源代码(反编译)是: 如果另一个线程在其被设置为默认值之后,在其被设置为解析值之前访问了结果,该怎么办 [编辑] 在佐伊德伯格的回答之后,我想重新表述一下这个问题 我想问题是,Enum.TryParse是否是“事务性的”(或原子性的) 假设我有一个静态字段,并将其传递到Enum.TryParse:C# Enum.TryParse-它是线程安全的吗?,c#,.net,.net-4.0,enums,tryparse,C#,.net,.net 4.0,Enums,Tryparse,我试图弄清楚.NET4.0的Enum.TryParse是否是线程安全的 源代码(反编译)是: 如果另一个线程在其被设置为默认值之后,在其被设置为解析值之前访问了结果,该怎么办 [编辑] 在佐伊德伯格的回答之后,我想重新表述一下这个问题 我想问题是,Enum.TryParse是否是“事务性的”(或原子性的) 假设我有一个静态字段,并将其传递到Enum.TryParse: public static SomeEnum MyField; .... Enum.TryParse("Value", out
public static SomeEnum MyField;
....
Enum.TryParse("Value", out MyField);
现在,当执行TryParse时,另一个线程访问MyField。TryParse会将MyField的值更改为SomeEnum的默认值一段时间,然后才将其设置为已解析的值
这不一定是我代码中的错误。我希望Enum.TryParse将MyField设置为已解析的值,或者根本不接触它,而不是将其用作其临时字段。
结果与其他所有局部变量和参数一样,是每次调用的结果。by ref参数的线程安全性要描述得复杂一些,但是:在每一种正常的使用中,这都不会是一个问题。我可以强制一个有风险的场景(由于副裁判传球),但这是一个人为的例子
典型用法:
SomeEnumType foo;
if(Enum.TryParse(s, true, out foo)) {...}
它是完全安全的
以下内容稍微复杂一些:
var objWithField = new SomeType();
// thread 1:
{
Enum.TryParse(x, true, out objWithField.SomeField));
}
// thread 2:
{
Enum.TryParse(y, true, out objWithField.SomeField));
}
并且不是线程安全的,但其原因比您在问题中描述的要微妙得多。是的-是的
您反编译的方法中根本没有共享状态
如果另一个线程调用同一个方法,它将获得它自己的所有局部变量的副本,result
由调用者传入
如果result
是一个类级变量,那么这将是一个问题,但这很好。真正的问题不是TryParse是否线程安全,而是使用它的代码(您的代码)是否线程安全。如果传入结果变量,然后让另一个线程在没有互斥保护的情况下访问它,那么可能会有问题,但问题会出现在代码中。结果(以及它引用的变量)可能会变成默认值(T),尽管字符串值包含一个不同的枚举值,如果这是您的意思的话
请尝试以下程序:
public enum FooBar
{
Foo = 0,
Bar
}
internal class Program
{
private static FooBar fb = FooBar.Bar;
private static void Main()
{
new Thread(() =>
{
while (true)
{
if (Program.fb == FooBar.Foo) // or try default(FooBar), which is the same
{
throw new Exception("Not threadsafe");
}
}
}).Start();
while (true)
{
if (!Enum.TryParse("Bar", true, out fb) || fb == FooBar.Foo)
{
throw new Exception("Parse error");
}
}
}
}
它迟早(可能更早)会抛出“非线程安全”异常。警告result
是按ref进行的,因此虽然指针的本地副本确实是隔离的,但底层值是按ref进行的,并且存在风险,但仅在非常人为的示例中。我接受您的观点。请注意,在这种情况下-result
是一个结构,这意味着您无论如何都不能修改任何人的副本,不是吗?因为它是按ref(out
)的,这正是我们可以修改其他人版本的原因:只有一个值(由ref传递的结构不会被复制;而是发送一个指向该值的指针)啊,是的,当然特别是因为它是通过引用传递的。这就是我的问题的真正意思——我将尝试修改这个问题。谢谢。其他.NET TryParse方法似乎也使用您的ref变量作为自己的临时存储。这不应该被认为是微软代码中的一个bug吗?当你要求将一个变量设置为一个值时,你通常不会期望它在被设置之前经历许多其他的状态。它被记录为线程安全的吗?如果不是,那么不管它是否真的是,您都不应该依赖它是线程安全的。
var objWithField = new SomeType();
// thread 1:
{
Enum.TryParse(x, true, out objWithField.SomeField));
}
// thread 2:
{
Enum.TryParse(y, true, out objWithField.SomeField));
}
public enum FooBar
{
Foo = 0,
Bar
}
internal class Program
{
private static FooBar fb = FooBar.Bar;
private static void Main()
{
new Thread(() =>
{
while (true)
{
if (Program.fb == FooBar.Foo) // or try default(FooBar), which is the same
{
throw new Exception("Not threadsafe");
}
}
}).Start();
while (true)
{
if (!Enum.TryParse("Bar", true, out fb) || fb == FooBar.Foo)
{
throw new Exception("Parse error");
}
}
}
}