Нужно ли синхронизировать доступ к аргументам и локальным методам при одновременном вызове метода?

1

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

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()) {
            (...)
        }
    }

Есть ли какая-либо причина, что создание экземпляра объекта блокировки в первый раз, когда он мне нужен, небезопасен? Будет ли использование оператора присваивания на моем объекте блокировки в моей инструкции блокировки каким-то образом вызвать у меня проблемы или предотвратить блокировку объекта?

  • 0
    Вы можете использовать static readonly object
  • 0
    Я предполагаю, что никогда не думал об использовании readonly на своих объектах блокировки, потому что я никогда не переназначаю их новым / различным объектам. Но не помешает ли это, в частности, сделать то, что я пытаюсь сделать? Я пытаюсь создать экземпляр объекта блокировки только в первом статическом методе, который блокирует этот объект; и минимизировать количество шагов / количество кода для ввода.
Теги:
thread-safety
concurrency
locking

3 ответа

2
Лучший ответ
  1. То, что у вас там, не будет иметь никакого значения вообще - сами аргументы не могут измениться после того, как звонок был сделан, поэтому он действительно ничего не делает. В вашем случае со строками, совершенно безопасно видеть, как строка является неизменной. Если это не так, возможно, что все, что передается, изменяется где-то в другом месте. В этом случае вам придется сделать реальную копию (т.е. Не просто копировать ссылку),

  2. Рассмотрим случай, когда два потока приходят к lock(staticLock1 = staticLock1?? new Object()) одновременно. Они оба могли видеть staticLock1 как null. Так что нет, что небезопасно!

  • 0
    Спасибо, Крис. Я полагаю, что вы ответили на оба моих вопроса полностью. Я просто хочу быть уверен в одном - рассмотрим этот сценарий: поток 1 вызывает StaticMethod1 ("teststring", 12). Итак, «заблокированная» обработка StaticMethod1 начинается с param1 = «teststring» и param2 = 12. Теперь поток 2 вызывает StaticMethod1 («newstring», 10). Я просто хочу подтвердить, что param1 не устанавливается в "newstring" во время обработки StaticMethod1 (как вызывается потоком 1). Я ценю вашу информацию, и я просто хотел убедиться, что я правильно понял ваш ответ.
  • 0
    Потоки имеют свой собственный контекст / состояние. Локальные переменные (которые являются аргументами) полностью отделены для каждого потока. Проблемы с потоками вступают в игру, когда несколько потоков одновременно получают доступ к некоторым общим данным.
Показать ещё 1 комментарий
1

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

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

Когда вы вызываете метод, аргументы имеют разные места хранения для каждого вызова. Они независимы. Поэтому два потока, вызывающие один и тот же метод, никогда не участвуют в доступе к аргументам метода. Поэтому вам не нужно синхронизировать доступ к аргументам метода.

То же самое касается локальных жителей. Фактически, аргументы имеют идентичные свойства синхронизации, как это делают локальные жители. Они существуют за звонок.

Чтобы ответить на вашу вторую проблему: это небезопасно, потому что два потока могут блокироваться для разных объектов. Кроме того, вы пишете на staticLock1 несинхронизированном по нескольким потокам. Я уже объяснил, что это гонка данных.

0

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

Просто выполните инициализацию следующим образом:

private readonly static object staticLock1 = new Object();

То, как вы используете оператор блокировки не является безопасным.

Во-вторых: я не вижу общих данных в методах, поэтому нет причин блокировать.

Наконец: я бы пересмотрел дизайн, если он содержит много статических функций.

  • 0
    Я сожалею, что мой пример кода выше может не полностью соответствовать моему реальному коду. Я уверен, что мне нужно блокировка в моем случае; Я продемонстрировал, что у моего приложения есть проблемы без него. Знаете ли вы, если у моего предложенного кода для создания экземпляра объекта блокировки в первый раз, когда он мне понадобится, возникнут проблемы? Является ли второй метод блокировки в staticLock1 менее поточно-безопасным, чем первый?
  • 0
    ваш способ тестирования и назначения в операторе блокировки неверен. Только представьте, что произойдет, если сразу после теста и перед тем, как взять блокировку, другой поток выполнит тот же тест. Кроме того, этот способ выполняет этот тест каждый раз при блокировке.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню