C # многопоточный код доступа к локальным переменным внутри блокировки - PullRequest
0 голосов
/ 14 марта 2012

Мой вопрос касается следующего фрагмента кода.Я упростил код, чтобы отогнать вопрос.

Я понимаю, что блокировка предотвращает изменение переменной Hashtable foo, но как насчет переменной вне блокировки?Мы видим странное поведение в коде, которое выглядит следующим образом, и это был вопрос, который возник.Спасибо за любой вклад.

using System.Collections;

namespace MultithreadScratch01
{
    public class ThreadFoo
    {
        public void Foo(Stub2 stub2, Hashtable foo)
        {
            Stub1 bar;

            var prop1 = stub2.Prop1;
            var prop2 = stub2.Prop2;
            var prop3 = stub2.Prop3;

            var hash = string.Format("{0}_{1}_{2}", prop1, prop2, prop3);

            lock(foo)
            {
                if(!foo.Contains(hash))
                {
                    bar = new Stub1 {Foo = "some arbitrary string", Bar = 123};
                    foo.Add(hash, bar);
                }
            }

        }
        public class Stub1
        {
            public string Foo { get; set; }
            public int Bar { get; set; }
        }
        public class Stub2
        {
            public string Prop1 { get; set; }
            public string Prop2 { get; set; }
            public string Prop3 { get; set; }

        }
    }
}

Ответы [ 4 ]

9 голосов
/ 14 марта 2012

То, что вы делаете здесь, является «худшей практикой» во многих отношениях.

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

Во-вторых, нет никакой гарантии, что другие потоки, имеющие ссылку на этот экземпляр хеш-таблицы, не блокируют его и не могут разблокировать его , потому что они глючат или враждебны. Вы, возможно, поручаете другим людям ответственность за правильность вашего кода, и это опасное положение. Хорошее правило - «никогда не блокируйте все, что может видеть внешний код». Никогда не блокируйте «это», никогда не блокируйте объект Type и т. Д. Есть времена, чтобы сделать исключение из этого правила, но я бы хотел вескую причину.

Правильнее всего сделать здесь, во-первых, использовать ConcurrentDictionary. Если вы не можете этого сделать, я бы написал обертку вокруг HashTable:

sealed class ThreadSafeHashTable
{
    private readonly HashTable hashTable = new HashTable();
    public void Add(object key, object value)
    {
        lock(this.hashTable)
        {
            ...

Теперь блокировка (1) всегда выполняется каждый раз, когда вызывается Add, и (2) только код в этом классе может взять блокировку, потому что заблокированный объект является закрытым и никогда не теряется.

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

6 голосов
/ 14 марта 2012

Неверно утверждать, что lock

предотвращает изменение переменной Hashtable foo

просто; никакие два потока, блокирующие один и тот же объект, не могут находиться внутри замка одновременно. Если у вас есть какой-либо код, который не lock на том же объекте (хеш-таблица), он сразу войдет и может привести к повреждению. Что касается других переменных ... если что-то мутирует объекты и не блокирует тот же объект блокировки, что и вы, вещи могут стать прикольными. На самом деле, есть даже некоторые крайние случаи, если заблокирован на одном и том же объекте (это было бы решено, если переместить свойство - читает внутри lock). Однако, поскольку эти переменные не «захвачены», после получения снимка значений снимок не изменится.

3 голосов
/ 14 марта 2012

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

Поскольку hash является локальной переменной, проблем с ней не должно быть.используя ConcurrentDictionary вместо Hashtable, он предназначен для многопоточного доступа.

1 голос
/ 14 марта 2012

Блокировка для параметра - опасная игра, на которую ссылались парни, приведенные выше, поскольку вы не знаете, что делается с этим параметром вне вашего метода. Кто-то другой может изменить его одновременно.

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

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