Singleton getInstance в методах потокового рабочего

Этот вопрос касается использования методов получения одноэлементного объекта в рабочих потоках. Вот сначала псевдокод:

// Singleton class which contains data
class MyData
{
    static MyData* sMyData ;

    int  mData1[1024];
    int  mData2[1024];
    int  mData3[1024];

    MyData* getInstance()
    {
        // sMyData is created in the very beginning.
        return sMyData ;
    }

    void getValues(int idx, int& data1,int& data2,int& data3)
    {
        data1 = mData1[idx];
        data2 = mData2[idx];
        data3 = mData3[idx];
    }

    int* getData1()
    {
        return &mData1[0];
    }
}

class MyThread
{
    void workerMethod()
    {
        MyData* md = MyData::getInstance();

        int d1,d2,d3;
        md->getValue( 12, d1,d2,d3 );

        int* data1 = md->getData1();
        d1 = data1[34];
    }
}

Как вы видите, у меня есть несколько методов получения (все только для чтения), MyData :: getInstance (), MyData :: getValue () и MyData :: getData1 (). Первый вопрос: насколько потокобезопасны эти методы?

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

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

Спасибо !

Павел

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
936
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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

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

Вам НЕ нужно блокировать только из-за умения. Блокировка нужна только в том случае, если запись не атомарна. Запись целых чисел атомарна. Если что-то должно записать 2 целых числа для поддержания согласованного состояния, вам понадобится блокировка, чтобы убедиться, что оба они обновлены, прежде чем кто-либо прочитает объект.

Martin York 07.11.2008 17:58

Ваши методы хороши, но у вас есть две основные проблемы.

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

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

НИКОГДА не используйте шкафчики с двойной проверкой. Его нельзя заставить работать на C++. См. aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

Martin York 07.11.2008 17:45

Я не согласен с тем, что никогда не следует использовать блокировку с двойной проверкой, хотя я понимаю и согласен с анализом Мейерса и Александреску, который является стандартом. На практике DCL - это почти идеальное решение проблемы «мой поток main () должен инициализировать объекты, а другие потоки их используют».

Clay 08.11.2008 17:24

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

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

Сначала вернитесь и прочтите этот вопрос, чтобы получить лучшую версию singelton:
Может ли кто-нибудь предоставить мне образец Синглтона на С ++?

Также обратите внимание: использует ли НЕТ синглтон как прославленную глобальную переменную. Это просто усложняет плохой дизайн. Просто используйте глобальную переменную.

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

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

Примечание: не соблазняйтесь использованием блокировки с двойной проверкой для оптимизации вашей стратегии блокировки. Можно заставить НЕТ правильно работать в C++. Прочтите это Статья DDJ.

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

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

  • Запись нескольких целых чисел в методе
  • Выполнение чтения, а затем записи

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

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

Для обеспечения безопасности потоков вам нужно смотреть на класс в целом. Как написано, ваш класс не будет потокобезопасным. Хотя ваш метод getValues ​​в порядке, у метода getData1 есть проблемы.

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

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

Другие вопросы по теме