Параллельно. Для неправильной обработки блокировки - PullRequest
3 голосов
/ 18 февраля 2012

Я прошел следующий тест:

private static object threadLocker = new object();

private static long threadStaticVar;
public static long ThreadStaticVar
{
    get
    {
        lock (threadLocker)
        {
            return threadStaticVar;
        }
    }
    set
    {
        lock (threadLocker)
        {
            threadStaticVar = value;
        }
    }
}

Parallel.For(0, 20000, (x) =>
{
    //lock (threadLocker) // works with this lock
    //{
        ThreadStaticVar++;
    //}
});

Этот Parallel.For вызывает метод, передавая значения от 0 до 19999. Таким образом, он будет выполнен 20 раз.

Если я не оберну ThreadStaticVar++; с lock, хотя он имеет блокировку на get и set, результат не будет 20000. Если я уберу комментарий и заблокирую его внутри .For, он получит правильное значение.

Мой вопрос: как это работает? Почему блокировка на get и set не работает? Почему это работает только внутри моего For?

Ответы [ 3 ]

5 голосов
/ 18 февраля 2012

Оператор ++ не является атомарным приращением.Будет вызов get, за которым последует вызов set, и эти вызовы могут чередоваться между различными потоками, поскольку блокировка выполняется только для каждой отдельной операции.Думайте об этом так:

lock {tmp = var}
lock {var = tmp+1}

Эти замки выглядят не так эффективно, не так ли?

2 голосов
/ 18 февраля 2012

В вашем примере ThreadStaricVar++ не является атомарной операцией .

Точнее, ++ не является атомарной операцией, поскольку она блокирует ваш геттер, затем увеличивает значение, а затем блокирует ваш сеттер для установки значения.Между этими двумя ситуациями может произойти все что угодно:)

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

1 голос
/ 18 февраля 2012

Вы можете переименовать threadStaticVar и сделать его общедоступным. Затем используйте Interlocked.Increment.

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

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