我正在试图弄清楚.NET 4.0的Enum.TryParse是否是线程安全的。
源代码(反编译)是:
[SecuritySafeCritical]
public static bool TryParse<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct
{
result = default(TEnum); /// (*)
Enum.EnumResult enumResult = default(Enum.EnumResult);
enumResult.Init(false);
bool result2;
if (result2 = Enum.TryParseEnum(typeof(TEnum), value, ignoreCase, ref enumResult))
{
result = (TEnum)enumResult.parsedEnum;
}
return result2;
}
对我来说似乎有问题的是这一行:
result = default(TEnum); /// (*)
如果另一个线程在被设置为默认值之后,并且在将其设置为已解析的值之前访问了结果,该怎么办?
[编辑] 按照Zoidberg的回答,我想稍微改一下这个问题。
问题是,我想,如果Enum.TryParse是“事务性的”(或原子的)。
假设我有一个静态字段,并将其传递给Enum.TryParse:
public static SomeEnum MyField;
....
Enum.TryParse("Value", out MyField);
现在,当执行TryParse时,另一个线程访问MyField。 TryParse会将MyField的值更改为SomeEnum的默认值一段时间,然后才会将其设置为已解析的值。
这不一定是我的代码中的错误。我希望Enum.TryParse能够将MyField设置为已解析的值,或者根本不触摸它,而不是将其用作临时字段。
答案 0 :(得分:7)
result
是每次调用。 by-ref参数的线程安全性是 little 更复杂的描述,但是:在每个合理的用法中 - 这不会是一个问题。我可以强制它有风险的场景(由于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));
}
并且不是线程安全的,但是比你在问题中描述的那些更微妙的原因。
答案 1 :(得分:5)
是的 - 是的。
您已反编译的方法中根本没有共享状态。
如果另一个线程调用相同的方法,它会获得所有本地的自己的副本,并且调用者传入result
。
如果result
是一个类级变量,那么这就是一个问题,但这很好。
答案 2 :(得分:3)
结果(因此它引用的变量)可以变为默认值(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");
}
}
}
}
它迟早(可能更早)抛出“Not threadsafe”异常。
答案 3 :(得分:2)
这不是TryParse是否是线程安全的问题,而是使用它的代码(您的代码)是否是线程安全的。如果您传入结果变量,然后让另一个线程访问它而没有一些互斥保护,那么您可能会遇到问题,但问题出在您的代码中。