5

我试图弄清楚 .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 设置为解析值或根本不触摸它,不将其用作临时字段。

4

4 回答 4

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

并且不是线程安全的,但出于比您在问题中描述的更微妙的原因。

于 2012-03-02T13:10:40.530 回答
5

是的。

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

如果另一个线程调用相同的方法,它会获得所有本地变量的自己的副本,result并由调用者传入。

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

于 2012-03-02T13:10:55.947 回答
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");
            }
        }
    }
}

它迟早会(可能更早)抛出“非线程安全”异常。

于 2012-03-02T13:21:13.957 回答
2

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

于 2012-03-02T13:13:19.910 回答