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 がスレッド セーフかどうかの問題ではなく、TryParse を使用するコード (コード) がスレッド セーフかどうかは問題です。結果変数を渡し、相互排除保護なしで別のスレッドからアクセスできるようにすると、問題が発生する可能性がありますが、問題はコードにあります。

于 2012-03-02T13:13:19.910 に答える