В качестве дополнения к ответам о том, почему статические методы не обязательно являются поточно-ориентированными, стоит подумать, почему они могут быть и почему они часто бывают.
Первая причина, по которой они могут быть, это, я думаю, тот случай, о котором вы думали:
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 гарантирует безопасность потоков в документации, и почти каждый член экземпляра не запрещает некоторые классы, специально предназначенные для одновременного использования (даже в некоторых случаях, когда член экземпляра фактически является потокобезопасным).
Причины двоякие:
Вид операций, наиболее часто используемых для статических членов, - это либо чистые функции (например, большинство статических членов класса Math
), либо чтение статических переменных только для чтения, которые не будут изменены другими нити.
Очень сложно перенести собственную синхронизацию на сторонние статические элементы.
Второй момент важен. Если у меня есть объект, члены экземпляра которого не являются поточно-ориентированными, то, если предположить, что вызовы не влияют на не-поточно-безопасные данные, совместно используемые различными экземплярами (возможно, но почти наверняка это плохой дизайн), тогда, если я хочу поделиться ими между я могу предоставить собственную блокировку для этого.
Если, однако, я имею дело со статическими членами, которые не являются поточно-ориентированными, мне будет гораздо сложнее это сделать. Действительно, учитывая, что я могу участвовать не только с собственным кодом, но и с кодом других сторон, это может оказаться невозможным. Это сделало бы любой такой открытый статический член бесполезным.
По иронии судьбы, причина того, что статические члены, как правило, являются потокобезопасными, заключается не в том, что их проще сделать (хотя это действительно относится к чистым функциям), а в том, что это труднее! На самом деле настолько тяжело, что автор кода должен сделать это для пользователя, потому что пользователь сам не сможет.