Потокобезопасность C# с получает/устанавливает

Часто используется термин «переполнение стека» (переполнение), но неправильное обозначение; атаки не переполняют стек, а буферы в стеке.

- из слайдов слайдов Prof. Доктор Дитер Голманн

49
задан mmr 3 February 2009 в 00:37
поделиться

7 ответов

Нет, Ваш код не заблокирует доступ к членам объекта, возвращенного от MyProperty. Это только блокирует MyProperty само.

Ваше использование в качестве примера является действительно двумя операциями одновременно, примерно эквивалентный этому:

// object is locked and then immediately released in the MyProperty getter
MyObject o = MyProperty;

// this assignment isn't covered by a lock
o.Field1 = 2;

// the MyProperty setter is never even called in this example

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

РЕДАКТИРОВАНИЕ в ответ на более подробную информацию в вопросе

я все еще не на 100% уверен, чего Вы пытаетесь достигнуть, но если Вы просто хотите атомарный доступ к объекту затем разве, у Вас не могло бы быть блокировки кода вызова против самого объекта?

// quick and dirty example
// there's almost certainly a better/cleaner way to do this
lock (MyProperty)
{
    // other threads can't lock the object while you're in here
    MyProperty.Field1 = 2;
    // do more stuff if you like, the object is all yours
}
// now the object is up-for-grabs again

Не идеальный, но пока весь доступ к объекту содержится в lock (MyProperty) разделы затем, этот подход будет ориентирован на многопотоковое исполнение.

39
ответ дан LukeH 7 November 2019 в 21:53
поделиться

Как другие указали, после того как Вы возвращаете объект из метода считывания, Вы теряете контроль над тем, кто получает доступ к объекту и когда. Чтобы сделать, что Вы желаете сделать, необходимо будет поместить блокировку в самом объекте.

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

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

Как пример...

public class Status
{
    private int _code;
    private DateTime _lastUpdate;
    private object _sync = new object(); // single lock for both fields

    public int Code
    {
        get { lock (_sync) { return _code; } }
        set
        {
            lock (_sync) {
                _code = value;
            }

            // Notify listeners
            EventHandler handler = Changed;
            if (handler != null) {
                handler(this, null);
            }
        }
    }

    public DateTime LastUpdate
    {
        get { lock (_sync) { return _lastUpdate; } }
        set { lock (_sync) { _lastUpdate = value; } }
    }

    public event EventHandler Changed;
}

Ваш поток 'опроса' выглядел бы примерно так.

Status status = new Status();
ManualResetEvent changedEvent = new ManualResetEvent(false);
Thread thread = new Thread(
    delegate() {
        status.Changed += delegate { changedEvent.Set(); };
        while (true) {
            changedEvent.WaitOne(Timeout.Infinite);
            int code = status.Code;
            DateTime lastUpdate = status.LastUpdate;
            changedEvent.Reset();
        }
    }
);
thread.Start();
4
ответ дан Matt Davis 7 November 2019 в 21:53
поделиться

В Вашей отредактированной версии Вы все еще не обеспечиваете ориентированный на многопотоковое исполнение способ обновить MyObject. Любые изменения в свойствах объекта должны будут быть сделаны в, синхронизировал/блокировал блок.

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

0
ответ дан jdigital 7 November 2019 в 21:53
поделиться

Объем блокировки в Вашем примере находится в неправильном месте - это должно быть в объеме свойства класса 'MyObject', а не это - контейнер.

, Если MyObject мой класс объекта просто используется для содержания данных, которые один поток хочет записать в, и другой (поток UI) для чтения с того времени Вас, возможно, не нуждался бы в методе set вообще и создал бы его однажды.

Также рассматривают, если размещение блокировок на уровне свойства является уровнем записи гранулярности блокировки; если больше чем одно свойство могло бы быть записано в то, для представления состояния транзакции (например: общие заказы и общий вес), затем могло бы быть лучше иметь блокировку на уровне MyObject (т.е. блокировку (myObject. SyncRoot)...)

2
ответ дан headsling 7 November 2019 в 21:53
поделиться

В примере кода Вы отправили, получение никогда не формуется.

В более сложном примере:

MyProperty.Field1 = MyProperty.doSomething() + 2;

И конечно принятие Вас сделал a:

lock (mLock) 
{
    // stuff...
}

В doSomething() затем все вызовы блокировки были бы не быть достаточными для гарантии синхронизации по всему объекту. Как только doSomething() функциональные возвраты, блокировка потеряна, затем дополнение сделано, и затем присвоение происходит, который блокирует снова.

Или, для записи это иначе, который можно симулировать как блокировки, не сделано amutomatically и переписывает это больше как "машинный код" с одной операцией на строку, и это становится очевидным:

lock (mLock) 
{
    val = doSomething()
}
val = val + 2
lock (mLock)
{
    MyProperty.Field1 = val
}
1
ответ дан SoapBox 7 November 2019 в 21:53
поделиться

Красота многопоточности состоит в том, что Вы не знаете, в котором произойдут вещи порядка. При установке чего-то на одном потоке это могло бы произойти сначала, это могло бы произойти после получения.

код Вы отправили с блокировкой участника, в то время как это читается и пишется. Если Вы хотите обработать случай, где значение обновляется, возможно, необходимо изучить другие формы синхронизации, такой как события . (Проверьте автоматические / версии руководства). Затем можно сказать потоку "опроса", что значение изменилось, и это готово быть перечитанным.

1
ответ дан OJ. 7 November 2019 в 21:53
поделиться

Параллельное программирование было бы довольно легко, если Ваш подход мог бы работать. Но это не делает, айсберг, который снижает тот Титаник, является, например, клиентом Вашего класса, делающего это:

objectRef.MyProperty += 1;

гонка read-modify-write довольно очевидна, существуют худшие. Нет абсолютно ничего, что можно сделать для создания свойства ориентированным на многопотоковое исполнение кроме создания его неизменный. Именно Ваш клиент должен иметь дело с головной болью. Будучи вынужденным делегировать такую ответственность перед программистом, который должен маловероятно разобраться, это - Ахиллесова пята параллельного программирования.

14
ответ дан Hans Passant 7 November 2019 в 21:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: