могут ли быть проблемы параллелизма при использовании класса C # только со статическими методами и без переменных? - PullRequest
12 голосов
/ 04 января 2012

Правильно ли я понял, что все потоки имеют копии переменных метода в своем собственном стеке, чтобы не было проблем при вызове статического метода из разных потоков?

Ответы [ 8 ]

16 голосов
/ 04 января 2012

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

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

Если каждый из этих экземпляров создается и используется only в потоке, использующем их, томаловероятно, что вы получите немного, но я просто хотел подчеркнуть, что только то, что вы используете статические методы только с местными параметрами / параметрами, не является гарантией поточной безопасности (то же самое с экземпляром, конечно,как отметил Крис).

9 голосов
/ 04 января 2012

Правильно ли я понял, что все потоки имеют копии переменных метода в своем собственном стеке, чтобы не было проблем при вызове статического метода из разных потоков?

Нет.

Во-первых, неверно, что «все потоки имеют копии локальных переменных метода в своем собственном стеке». Локальная переменная генерируется в стеке только тогда, когда она имеет короткое время жизни; локальные переменные могут иметь произвольно большое время жизни, если они (1) закрытые внешние переменные, (2) объявлены в блоке итератора или (3) объявлены в асинхронном методе.

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

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

могут ли быть проблемы параллелизма при использовании класса C # только со статическими методами и без переменных?

Полагаю, вы имеете в виду «нет статических переменных», а не «нет локальных переменных».

Абсолютно может быть . Например, вот программа без статических переменных, нестатических методов, без объектов, созданных отдельно от второго потока, и единственная локальная переменная для хранения ссылки на этот поток. Ни один из методов, кроме cctor, на самом деле вообще ничего не делает . Эта программа блокируется. Вы не можете предполагать, что только потому, что ваша программа очень проста и не содержит ошибок в потоке!

Упражнение для читателя: опишите, почему эта программа, которая, по-видимому, не содержит блокировок, фактически блокируется.

class MyClass
{
  static MyClass() 
  {
      // Let's run the initialization on another thread!
      var thread = new System.Threading.Thread(Initialize);
      thread.Start();
      thread.Join();
  }

  static void Initialize() 
  { /* TODO: Add initialization code */ }

  static void Main() 
  { }
}

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

2 голосов
/ 04 января 2012

Нет такой гарантии, если все переменные не являются неизменяемыми ссылочными типами или типами значений.

Если переменные являются изменяемыми ссылочными типами, необходимо выполнить правильную синхронизацию.

РЕДАКТИРОВАТЬ:Изменяемые переменные необходимо синхронизировать только в том случае, если они разделены между локально объявленными изменяемыми таблицами, которые не предоставляются вне метода, и синхронизировать их не нужно.

0 голосов
/ 06 января 2012

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

Первая причина, по которой они могут быть, это, я думаю, тот случай, о котором вы думали:

public static int Max(int x, int y)
{
  return x > y ? x : y;
}

Эта чистая функция является поточно-ориентированной, поскольку у нее нет возможности повлиять на код в любом другом потоке, локальные x и y остаются локальными по отношению к той команде, в которой они находятся, и не хранятся в общей папке. , захваченный в делегате или иным образом оставляющий чисто локальный контекст.

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

Статический метод также может гарантировать собственную безопасность потока:

public object GetCachedEntity(string key)
{
  object ret;  //local and remains so.
  lock(_cache) //same lock used on every operation that deals with _cache;
    return _cache.TryGetValue(key, out ret) ? ret : null;
}

OR

public object GetCachedEntity(string key)
{
  object ret;
  return _cache.TryGetValue(key, out ret) ? ret : null; //_cache is known to be thread-safe in itself!
}

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

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

Причины двоякие:

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

  2. Очень сложно перенести собственную синхронизацию на сторонние статические элементы.

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

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

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

0 голосов
/ 04 января 2012

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

Марка

0 голосов
/ 04 января 2012
Методы

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

Так что, в конечном счете, ответ на ваш вопрос "нет ", потому что могут быть проблемы, хотя обычно их не будет.

0 голосов
/ 04 января 2012

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

0 голосов
/ 04 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...