Этот вопрос касается использования методов получения одноэлементного объекта в рабочих потоках. Вот сначала псевдокод:
// 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 (). Первый вопрос: насколько потокобезопасны эти методы?
Поскольку их часто называют методами, я стараюсь избегать защиты этих методов с помощью мьютексов.
Второй вопрос: каков предлагаемый способ чтения данных из центральных источников в многопоточном приложении, особенно в рабочих методах.
Спасибо !
Павел





При условии, что никакой другой поток не будет пытаться записывать данные в вашем одноэлементном объекте, вам не нужно защищать их: по определению, несколько считывателей в отсутствие писателя являются потокобезопасными. Это распространенный шаблон, когда код инициализации программы устанавливает синглтон, который затем читается только рабочими потоками.
Однако, если поток ЛюбыеКогда-либо записывает эти данные, в то время как другие читают их, вы должны каким-то образом защитить его. Если у вас много читателей и вы пишете только время от времени, стоит подумать о какой-то блокировке «чтение-запись», которая позволяет нескольким читателям при отсутствии писателей.
Ваши методы хороши, но у вас есть две основные проблемы.
Во-первых, MyData :: getInstance () должен быть статическим и не создавать экземпляр. Чтобы свести к минимуму использование мьютекса, вы должны проверить замок с двойной проверкой.
Во-вторых, безопасность потоков зависит от вызывающих методов получения, а не от класса MyData. Если это то, что вы ищете, тогда отлично. Если нет, то вам придется придумать какой-то контроль доступа в MyClass. Кроме того, поскольку ваш содержащийся тип данных является просто базовым типом (int), велики шансы, что вы никогда не увидите никаких проблем с синхронизацией до тех пор, пока ваш код не будет запущен в производство.
НИКОГДА не используйте шкафчики с двойной проверкой. Его нельзя заставить работать на C++. См. aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
Я не согласен с тем, что никогда не следует использовать блокировку с двойной проверкой, хотя я понимаю и согласен с анализом Мейерса и Александреску, который является стандартом. На практике DCL - это почти идеальное решение проблемы «мой поток main () должен инициализировать объекты, а другие потоки их используют».
Невозможно сказать, является ли это потокобезопасным. Если данные инициализируются во время создания объекта и никогда не изменяются, это будет работать правильно. Если вы изменяете базовые данные с помощью других методов, тогда читателям придется выполнять какую-то синхронизацию с писателями, пути нет.
В зависимости от того, что именно вы делаете, вы можете использовать что-то более легкое, чем мьютекс, например, команды синхронизации атомарных обновлений или блокировки чтения-записи, но, не зная больше о том, что вы делаете, это невозможно сказать.
Сначала вернитесь и прочтите этот вопрос, чтобы получить лучшую версию singelton:
Может ли кто-нибудь предоставить мне образец Синглтона на С ++?
Также обратите внимание: использует ли НЕТ синглтон как прославленную глобальную переменную. Это просто усложняет плохой дизайн. Просто используйте глобальную переменную.
Пока вы читаете только из синглтона, он является потокобезопасным при использовании.
Единственное, что не является потокобезопасным (не гарантируется языком), - это процесс создания. Таким образом, технически вы должны добавить блокировки вокруг части, которая создает экземпляр, чтобы гарантировать, что синглтон был полностью создан, прежде чем кто-либо сможет его использовать.
Примечание: не соблазняйтесь использованием блокировки с двойной проверкой для оптимизации вашей стратегии блокировки. Можно заставить НЕТ правильно работать в C++. Прочтите это Статья DDJ.
Если вы можете гарантировать, что синглтон создается (вероятно, первый вызов также getInstance ()) в однопоточной среде (до создания каких-либо потоков), тогда вы можете обойтись без блокировок во время создания экземпляра.
Если вы измените свой код, чтобы другие потоки могли писать в синглтон, вам нужно подумать о блокировке и согласованности. Блокировка нужна только в том случае, если ваши записи не атомарны. Если ваша запись просто обновляет одно целое число, оно уже атомарно, никаких изменений не требуется. Если ваш код не атомарный:
Затем вам нужно будет заблокировать объект до тех пор, пока все записи не будут завершены, и, следовательно, также заблокировать методы, которые имеют доступ для чтения, чтобы остановить их чтение из объекта, пока другой метод не обновляет объект.
Следовательно, это еще одна причина, по которой к переменным-членам следует обращаться только с помощью методов-членов.
Для обеспечения безопасности потоков вам нужно смотреть на класс в целом. Как написано, ваш класс не будет потокобезопасным. Хотя ваш метод getValues в порядке, у метода getData1 есть проблемы.
Вы говорите, что это методы получения (только для чтения). Однако ни один из них не объявлен как константный метод. GetData1 не будет действительным как константный метод, поскольку он возвращает неконстантный указатель. Кроме того, возвращение указателя на данные частного класса - это плохо, поскольку вы раскрываете свою реализацию.
Если это одноэлементный класс для хранения некоторого, по существу, статического набора данных при инициализации до запуска потоковой передачи, тогда все ваши методы доступа должны быть константными методами. GetInstance также должен возвращать константный указатель на класс (и быть статическим методом, как указано в другом ответе).
Вам НЕ нужно блокировать только из-за умения. Блокировка нужна только в том случае, если запись не атомарна. Запись целых чисел атомарна. Если что-то должно записать 2 целых числа для поддержания согласованного состояния, вам понадобится блокировка, чтобы убедиться, что оба они обновлены, прежде чем кто-либо прочитает объект.