Enum.TryParse - это потокобезопасность? - PullRequest
5 голосов
/ 02 марта 2012

Я пытаюсь выяснить, является ли Enum.TryParse в .NET 4.0 поточно-ориентированным.

Исходный код (декомпилированный):

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

Что кажется проблематичнымme это следующая строка:

result = default(TEnum);   /// (*)

Что если другой поток обращается к результату сразу после того, как ему присвоено значение по умолчанию, и до того, как ему присвоено проанализированное значение?

[EDIT]Ответ Зойдберга, я бы хотел немного перефразировать вопрос.

Вопрос, я полагаю, если Enum.TryParse является "транзакционным" (или атомарным).

Скажите, что у меня естьстатическое поле и передайте его в Enum.TryParse:

public static SomeEnum MyField;
....
Enum.TryParse("Value", out MyField);

Теперь, когда выполняется TryParse, другой поток обращается к MyField.TryParse на какое-то время изменит значение MyField на значение по умолчанию SomeEnum, и только затем установит для него проанализированное значение.

Это не обязательно ошибка в моем коде.Я бы ожидал, что Enum.TryParse либо установит MyField на проанализированное значение, либо вообще не прикасается к нему, а не использует его в качестве временного поля.

Ответы [ 4 ]

7 голосов
/ 02 марта 2012

result, как и с любой другой локальной переменной и параметром, является для каждого вызова. Потоковая безопасность параметра by-ref является немного более сложной для описания, но: при любом нормальном использовании - это не будет проблемой. Я мог бы форсировать сценарий, в котором он подвергался риску (из-за прохождения 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 голосов
/ 02 марта 2012

Да - это так.

В методе, который вы декомпилировали, вообще нет общего состояния.

Если другой поток вызывает тот же метод, он получает свою собственную копию всех локальных объектов, и вызывающая сторона передает result.

Если бы result была переменной уровня класса, это было бы проблемой, но это нормально.

3 голосов
/ 02 марта 2012

результат (и, следовательно, переменная, на которую он ссылается) может стать значением по умолчанию (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");
            }
        }
    }
}

Рано или поздно (вероятно, рано) возникнет исключение "Не поточно-безопасный".

2 голосов
/ 02 марта 2012

Вопрос не в том, является ли TryParse потокобезопасным, а в том, является ли код, использующий его (ваш код), потокобезопасным.Если вы передадите переменную результата, а затем разрешите доступ к ней другому потоку без какой-либо защиты от взаимного исключения, у вас может возникнуть проблема, но проблема будет в вашем коде.

...