Подтвердить что ты не робот

Может возникнуть проблема concurrency при использовании класса С# только с статическими методами и без переменных?

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Нет.

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

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

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

может ли быть проблема concurrency при использовании класса С# только с статическими методами и без переменных?

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

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

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

class MyClass
{
  static MyClass() 
  {
      // Let 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() 
  { }
}

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

Ответ 3

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

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

EDIT: изменяемые переменные должны быть синхронизированы только в том случае, если они разделены между потоками. Локально объявленные переменные, которые не отображаются вне метода, не нужно синхронизировать.

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Марк

Ответ 8

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

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

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

ИЛИ

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 гарантирует безопасность потоков в документации, и почти каждый член экземпляра не запрещает некоторые классы, специально предназначенные для одновременного использования (даже в некоторых случаях, когда элемент экземпляра фактически является потокобезопасным).

Причины в два раза:

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

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

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

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

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