Я писал много кода со статическими классами/методами, которые, как я полагаю, будут вызываться/выполняться одновременно несколькими потоками. Поэтому я много блокирую свои методы. Обычно я это делаю:
public static class MyThreadsafeMethods {
private static Object staticLock1 = new Object();
private static Object staticLock2 = new Object();
public static string StaticMethod1(string param1, int param2) {
lock (staticLock1) {
var _param1 = param1;
var _param2 = param2;
//highly confidential business logic here
return StaticMethod2(_param1, "Integer: " + _param2.ToString());
}
}
public static string StaticMethod2(string param1, string param2) {
lock (staticLock2) {
var _param1 = param1;
var _param2 = param2;
//truly groundbreaking algorithm here
return _param1 + " - " + _param2;
}
}
}
Мне интересно две вещи:
1) Я думал, что мне нужно работать с локальными "копиями" моих параметров внутри "заблокированного кода"; потому что если другой поток вызывает мой метод с разными значениями для param1 и param2, это может испортить мою обработку. Если я работаю только с переменными, которые объявлены/созданы внутри заблокированного кода (то есть _param1 и _param2 в приведенном выше примере), тогда что-то может изменить значения param1 и param2 (или отправить ссылки на разные объекты), и я в порядке. Но нужно ли мне это делать? Неужели я излишне параноик?
2) Я решил, что я не хочу создавать экземпляры объектов блокировки до тех пор, пока они мне не понадобятся, потому что моя коллекция объектов статической блокировки растет... Итак, я сейчас перехожу к этому:
private static Object staticLock1;
public static string StaticMethod1(string param1, int param2) {
lock(staticLock1 = staticLock1 ?? new Object()) {
(...)
}
}
Есть ли какая-либо причина, что создание экземпляра объекта блокировки в первый раз, когда он мне нужен, небезопасен? Будет ли использование оператора присваивания на моем объекте блокировки в моей инструкции блокировки каким-то образом вызвать у меня проблемы или предотвратить блокировку объекта?
То, что у вас там, не будет иметь никакого значения вообще - сами аргументы не могут измениться после того, как звонок был сделан, поэтому он действительно ничего не делает. В вашем случае со строками, совершенно безопасно видеть, как строка является неизменной. Если это не так, возможно, что все, что передается, изменяется где-то в другом месте. В этом случае вам придется сделать реальную копию (т.е. Не просто копировать ссылку),
Рассмотрим случай, когда два потока приходят к lock(staticLock1 = staticLock1?? new Object())
одновременно. Они оба могли видеть staticLock1
как null. Так что нет, что небезопасно!
Ваша основная путаница, похоже, связана с тем, какая синхронизация вам нужна, чтобы безопасно вызвать статический метод одновременно.
Гонки данных всегда возникают, поскольку несколько потоков обращаются к одному и тому же месту хранения несинхронизированным образом, и по крайней мере один из них является писателем. Это основное правило достаточно, чтобы рассуждать о многих проблемах параллелизма.
Когда вы вызываете метод, аргументы имеют разные места хранения для каждого вызова. Они независимы. Поэтому два потока, вызывающие один и тот же метод, никогда не участвуют в доступе к аргументам метода. Поэтому вам не нужно синхронизировать доступ к аргументам метода.
То же самое касается локальных жителей. Фактически, аргументы имеют идентичные свойства синхронизации, как это делают локальные жители. Они существуют за звонок.
Чтобы ответить на вашу вторую проблему: это небезопасно, потому что два потока могут блокироваться для разных объектов. Кроме того, вы пишете на staticLock1
несинхронизированном по нескольким потокам. Я уже объяснил, что это гонка данных.
Во-первых: накладные расходы на создание экземпляра объекта чрезвычайно малы. Не беспокойтесь об этом, если измерения не покажут вам, что вы должны.
Просто выполните инициализацию следующим образом:
private readonly static object staticLock1 = new Object();
То, как вы используете оператор блокировки не является безопасным.
Во-вторых: я не вижу общих данных в методах, поэтому нет причин блокировать.
Наконец: я бы пересмотрел дизайн, если он содержит много статических функций.
static readonly object