2012-03-02 49 views
5

我想弄清楚如果.NET 4.0的Enum.TryParse是线程安全的。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

似乎其他.NET TryParse方法也会将自己的ref变量用于其自己的临时存储。这不应该被认为是微软代码中的一个错误吗?当你要求将一个变量设置为一个值时,你通常不会期望它在经过设定之前经过其他许多状态。 – 2012-03-02 13:41:20

+2

它被记录为线程安全吗?如果不是,那么**不管它是否真的是,你不应该依赖它是线程安全**。 – 2012-03-02 18:40:03

回答

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"); 
      } 
     } 
    } 
} 

这将迟早(可能更早)抛出了“不是线程安全”异常。

+0

这正是我的观点。谢谢。 – abieganski 2012-03-02 13:24:40

7

result与其他每个局部变量和参数一样,都是每次调用。 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)); 
} 

,而不是线程安全的,但比那些更微妙的原因,你在问题描述。

5

是的 - 确实如此。

在您反编译的方法中根本没有共享状态。

如果另一个线程调用相同的方法,它将获得它自己的所有当地人的副本,调用者传入result

如果result是一个类级别的变量,那么这将是一个问题,但这很好。

+3

警告; 'result'是by-ref,因此尽管指针的本地副本确实是孤立的,但其基础值是by-ref并处于风险中,但仅限于非常人为的例子。 – 2012-03-02 13:13:45

+0

我明白你的观点。请注意,在这种情况下,'result'是一个结构体,所以这意味着你无法改变任何人的副本不是吗? – 2012-03-02 13:15:56

+0

因为它是by-ref('out')**正是为什么**我们可以改变其他人的版本:只有一个值(通过-ref传递的结构不被复制;指向该值的*指针*被发送相反) – 2012-03-02 13:17:28

2

真的不是TryParse是否是线程安全的问题,但是使用它的代码(您的代码)是否是线程安全的。如果你传递了结果变量,然后让它被另一个线程访问而没有一些互斥保护,那么你可能会遇到问题,但问题将出现在你的代码中。

+0

这就是我的问题 - 我会尝试修改问题。谢谢。 – abieganski 2012-03-02 13:14:53