Nano Hash - криптовалюты, майнинг, программирование

C # Требуется ли блокировка в геттерах и сеттерах?

Нужно ли вообще блокировать геттеры и сеттеры, когда несколько потоков будут обращаться к свойству / полю через get / set-functions.

Например:

У вас есть таймер, который регулярно получает значение объекта. И процесс, который регулярно обновляет это значение.

public class Task : IProgressProvider
{
    ...
    public Progress GetProgress()
    {
        // should i lock here?
        return _progress;
    }

    public SetProgress(Progress progress)
    {
        // and lock here?
        _progress = progress;
    }

    public void Execute()
    {
        // ...
        SetProgress(new Progress(10));
        // ...
        SetProgress(new Progress(50));
        // ...
        SetProgress(new Progress(100));
    }
}

public class ProgressReporter
{
    public ProgressReporter()
    {
        _timer = new Timer();
        _timer.Elapsed += Elapsed;
        // ...
    }

    // ...

    public void Elapsed(...)
    {
        var progress = _task.GetProgress();
        // ...
    }
}

Еще раз вопрос:

  • Следует ли заблокировать функции GetProgress и SetProgress.
  • Если да, то почему?
  • Требуется ли в этом случае энергозависимость (при условии, что одно или несколько ядер процессора)
  • Для простоты предположим, что свойства / поля из Progress доступны только для чтения. РЕДАКТИРОВАТЬ: я знаю, что + = и т. д. не являются атомарными операциями и требуют правильной обработки (например, блокировки)

Я бы подумал, что установка и чтение из переменной _progress - это атомарная операция. Не важно, что GetProgress получит самое актуальное значение. Если не в этот раз, он получит его при следующем вызове GetProgress.


  • Если _progress является ссылочным типом, то чтение и запись его значения (т.е. изменение ссылки) действительно является атомарной операцией, поэтому вам не нужно блокировать конкретный код в вашем примере. Но если вы когда-либо изменяли более одного поля в установщике или получателе, или если это поле не было типом с атомарным чтением / записью (например, double), вам необходимо заблокировать. 20.06.2013
  • @MatthewWatson: Атомарность - это только часть уравнения. Если вы хотите, чтобы все потоки читали последнее записанное значение, вам, вероятно, потребуется блокировка. (Или явный барьер памяти.) 20.06.2013
  • Начните с blogs.msdn.com/b/ericlippert/archive/2011/05/26/ 20.06.2013
  • @JonSkeet Да, но для таких вещей поле все равно может измениться через микросекунду после того, как оно было возвращено. Если вы хотите, чтобы несколько потоков наблюдали одно и то же значение в любой момент, вам действительно потребуется дополнительная блокировка. Если вам все равно, может ли один поток прочитать одно значение, а другой поток - другое значение (потому что оно изменилось между ними), то блокировка действительно не кажется необходимой. OP говорит, что не важно, что GetProgress получит самое актуальное значение. Если не в этот раз, он получит это при следующем вызове GetProgress, поэтому в этом случае дополнительная блокировка не требуется. 20.06.2013
  • @MatthewWatson: _progress - это ссылочный тип, да. Самое последнее значение необходимо только в том случае, если процесс должен завершиться. Но это не проблема, если это произойдет через несколько показаний после того, как оно было установлено. Только импорт, что последнее значение будет когда-либо доступно. Предположение: после того, как прогресс установлен в состояние завершения, значение никогда не изменится снова. 20.06.2013
  • @MatthewWatson: Я хочу поблагодарить вас за то, что вы ответили на мой вопрос. Не могли бы вы собрать свои комментарии в качестве ответа? 20.06.2013
  • @SACO Хорошо, я так и сделал. 20.06.2013

Ответы:


1

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

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

Я бы сделал его непостоянным. volatile предназначен именно для этой цели. Это, по крайней мере, предотвратит кэширование оптимизирующим компилятором значения, когда этого не следует (в случае, если он когда-либо сделает такое).

Итак, подведем итог: для вашего необходимого использования вы должны сделать _progress изменчивым, но вам не нужно блокировать доступ к нему.

20.06.2013

2

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

Если у вас есть несколько потоков, обновляющих переменную Progress, вам все равно не потребуется блокировка в функции set, поскольку она атомарна (если это ссылочная переменная, как это выглядит).

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

    public Progress IncrProgress(int incr)
    {
        lock (_progressLock)
        {
            // Get the current progress
            int current = _progress.GetPercentage();
            current += incr;
            _progress = new Progress(current);
        }
        return _progress;
    }

Что касается вашего другого вопроса, _progress следует пометить как изменчивый, поскольку он обновляется другим потоком.

http://msdn.microsoft.com/en-us/library/x13ttww7(v=vs.71).aspx

20.06.2013
  • Я предположил, что объект неизменен. См. Выше. 20.06.2013
  • Исходя из этого, вам, вероятно, не нужны никакие блокировки, а только изменчивый флаг при чтении. 20.06.2013

  • 3

    Я не думаю, что свойства / поля имеют значение только для чтения, если никто никогда не собирается изменять состояние объектов.

    Так что, если все, что у вас есть, - это прогресс настройки одного потока, а другие его читают - блокировка не требуется.

    Даже если это так, сделать объект Progress неизменяемым было бы неплохо, потому что в проекте вы прямо указываете: почему вам не нужны никакие блокировки? Потому что объект не может измениться.

    Кроме того, если есть более поздние изменения / перепроектирование (которые решают изменить / видоизменить объект выполнения), вы не можете случайно внести ошибки потоковой передачи.

    Даже ключевое слово volatile может не обязательно? Поскольку вам все равно, потеряете ли вы одну или две отметки прогресса. Ключевое слово посвящено «сериализации доступа» к полю.

    20.06.2013
  • Я бы сделал его непостоянным. Это как раз для этого. Это, по крайней мере, предотвратит кэширование оптимизирующим компилятором значения, когда этого не следует (в случае, если он когда-либо сделает такое). 20.06.2013
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..