Безопасность потоков .NET - PullRequest
       2

Безопасность потоков .NET

1 голос
/ 23 августа 2010

Конструктор ApplicationException является членом экземпляра, поэтому он не гарантированно является потокобезопасным.Как узнать, что следующий код совершенно не нужен?Когда я говорю «знаю», я имею в виду, есть ли в документации что-то, что это не нужно, или вы видели исходный код .NET и знаете, что это не нужно?

// thread-safe
internal static class TsApplicationException
{
    private readonly static object myLock = new object();

    internal static void Throw(string msg, Exception e)
    {
        lock (myLock)
        {
            throw new ApplicationException(msg, e);
        } 
        // the implementation of lock() ensures that the unlock will happen
        // even if there is an exception thrown
    }
}

Править: Эта проблема не является беспочвенной, поскольку в документации прямо указывается, что это может быть необходимо.Существует вероятность того, что эта проблема вызвана довольно распространенной ошибкой документации.

Редактировать: Ниже было предложено, что помимо моих потоков, под которыми я подразумеваю код, я непосредственно отвечаю заПлюс код, который я никогда не видел, но, увы, я косвенно отвечаю за клиента .NET и Windows - на моем компьютере работает несколько потоков, таких как браузеры, Office и музыкальные проигрыватели, и все же он работает нормально.Я думаю, в идеале я искал что-то другое, чем доказательство на примере.Возможно, люди, которые написали мой музыкальный проигрыватель, тоже блокируют это;) В этом случае это вовсе не доказательство.

Я бы также добавил, что в этом вопросе может быть два варианта.Одним из них является вопрос о том, что потоки одного приложения (с вещами, происходящими с потоками .NET и Windows) являются звуковыми, а другой - о том, что все потоки всех приложений на данном компьютере являются звуковыми (сосуществование друг с другом, а также .NET и Windows).Мой вопрос относится к первому случаю.Я не делаю предположений о том, насколько хорошо Windows сохраняет приложения изолированными, и это не мое беспокойство в отношении публикации этого вопроса.

Наконец, если эта блокировка необходима, то для платформы .NET Framework и третьей нужно много других блокировокиспользование объектаПример: документация MySQL также содержит ту же строку о методах экземпляра, которые «не гарантируют поточно-ориентированный».В некоторых случаях .NET и, возможно, в большинстве случаев MySQL, риск связан с тем, что, вероятно, гораздо более функционально, чем простое исключение.

Ответы [ 3 ]

4 голосов
/ 23 августа 2010

Во-первых, элементы экземпляра могут быть потокобезопасными. Достаточно просто убедиться, что статические члены являются поточно-ориентированными, но не предоставляют те же гарантии для членов экземпляров по причинам, которые мы обсуждали ранее.

Во-вторых, конструктор не вызывается в контексте, где более одного потока могут получить доступ к одному экземпляру. Единственная ссылка на вновь созданное исключение ApplicationException в этот момент является локальной для вызывающего метода и, следовательно, видимой только для одного потока. Если два потока одновременно обращаются к методу Throw, у них будет создано два отдельных экземпляра.

Поэтому, хотя он сам по себе не является потокобезопасным, он не используется в контексте, доступном для нескольких потоков, и, следовательно, нет необходимости блокировать его.

Более важный случай:

void MyFunctionOfThreadSafety(string someStr, int someInt, bool someBool)
{
  if(someBool)// №1
  {
    var dict = new Dictionary<string, int>();
    dict[someStr] = someInt; // №2
    int test = 0;
    if(!dict.TryGetValue(someStr, out test))// №3
      throw new Exception("really unexpected");
    dict[someStr] = test + someInt; // №4
  }
}

В этом коде строки, прокомментированные с №1 по №4, - это места, где, если рассматриваемые объекты доступны более чем одному потоку, тогда проблемы безопасности потока могут вызвать проблемы (действительно, все кроме №1 предлагают больше одна точка, в которой потоки могут переключаться, и все становится странным).

Этот код, тем не менее, полностью поточно-ориентирован. Хотя он выполняет несколько потенциально небезопасных действий с объектами, ни один из этих объектов не может быть изменен другим потоком.

someInt и someBool - параметры типа значения, которые не были переданы byref или out. Поэтому они существуют только в контексте этого метода (и методов, которые он вызывает, если передает их на byref или out).

someStr - это ссылочный тип, передаваемый в качестве параметра, что означает, что он потенциально может также храниться где-то, что может получить другой поток. Однако, поскольку он является неизменным, нет опасности, что другой поток записывает в него (так как никакой поток вообще не может писать в него). Чтения не должны быть защищены от чтения, только от записи (хотя, когда у вас есть чтение и запись, вам может потребоваться заблокировать как чтение, так и запись). Поскольку операции someStr могут быть прочитаны, это безопасно.

dict и test созданы в этом методе, поэтому они безопасны. Если dict было возвращено из метода, то в качестве изменяемого ссылочного типа он мог бы, если бы он хранился где-то видимым для нескольких потоков, стать проблемой безопасности потока. Но (а) это не было и (б) это не будет проблемой до после это произошло; здесь это потокобезопасно, несмотря ни на что.

2 голосов
/ 23 августа 2010

В этом нет необходимости, поскольку свойства в объекте ApplicationException не будут изменены, и я считаю, что все свойства в объектах исключений доступны только для чтения.

Поэтому потокобезопасность не имеет значения.

0 голосов
/ 23 августа 2010

Единственные конструкторы времени не являются поточно-ориентированными, когда они изменяют статические данные в своем классе.Это крайне редко, поэтому блокировка не нужна.На самом деле, вы только блокируете использование конструктора, поэтому любой другой поток, не работающий в TsApplicationException, может в любом случае одновременно выбросить ApplicationException.

...