![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| SABROG |
|
||||||
![]() Hacker ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 2481 Регистрация: 18.9.2006 Репутация: 4 Всего: 91 |
Прочитал статью. Всё обсуждение идет на примере Java, но основные вещи применимы к C++. Если кратко, то из этой статьи я вынес следующие 3 проблемы в параллельных вычислениях:
Собственно первая проблема решается через кеширование указателя на данные, чтобы присвоить его глобальной переменной после выполнения конструктора. Тут вытекает проблема номер 2 с возможной оптимизацией компилятора, которая решается через объявление переменной с ключевым словом volatile. Далее идут непонятки. Автор статьи пишет:
То есть проблема уже не в последовательной консистенции, а в том, что перемещается сам код. Куда, зачем и каким образом он не указал, поэтому тут остается пробел. Статья сама заканчивается на плохой ноте о несостоятельности паттерна Double-Checking Locking, и вместо него он предлагает Java программистам использовать static переменную. Теперь возвращаясь к C++. Все знают паттерн Singleton, который в не тех руках быстро превращается в анти-паттерн. Большинство примеров в интернете не потокобезопасные и не реентерабельные. Есть вариант в функции getInstance() которого используется boost::call_once() или std::call_once() из нового стандарта C++0x. Сам исходный код этой функции находится тут. Выглядит он так:
Если внимательно посмотреть, то можно выделить характерные черты паттерна Double-Checking Locking. А также то, что тип fast_pthread_once_t является атомарным и volatile. В принципе в этом коде нет первых двух проблем о которых я говорил в начале поста. Компилятор атомарные типы выкидывать не будет, динамически созданных объектов и передачи ссылок на него в глобальную переменную тут нет. А вот есть ли тут нарушение последовательной консистенции (Sequential Consistency)? Например возможны ли ситуации, когда эти 3 строчки будут выполняться в разной последовательности и это может привести к плохим последствиям?
Предположим первый поток пропускает 5 и 9 строку и сразу инициализирует *once = global_epoch. В этом случае, когда второй поток вытеснит первый он сравнит *once, в котором будет старое значение global_epoch (0 - на момент первой инициализации) и неопределенной значение находящееся в переменной _fast_pthread_once_per_thread_epoch, так как она никогда не инициализировалась (почему? У __thread переменных есть default initialized value?). Итак x == 0, а _fast_pthread_once_per_thread_epoch, предположим, тоже 0. Тогда ничего не произойдет. Если _fast_pthread_once_per_thread_epoch будет -1, тогда поток залочится на мутексе. Вроде бы ничего страшного. Тогда предположим, что поток выполняет 9 строчку первой. В этом случае тоже ничего страшного не будет, так как global_epoch нигде не проверяется за пределами мутекса. Собственно 3 вопроса. - обеспечивают ли атомарные переменные/операции в C++ соблюдение последовательной консистенции? - если не обеспечивают, то каким образом этого добиться? - почему переменную global_epoch не сделали 3-state: BEING_INITIALIZED, FAST_PTHREAD_ONCE_INIT, INITIALIZED. Для чего её накручивать? В статье говорится, чтобы считать количество проинициализированных функций вызывающих once_call. Если флаги будут динамическими, а не статическими и будут вызываться масса различных функций, то счетчик рано или поздно пойдет на второй круг, а 2 вызова приходящиеся на значения BEING_INITIALIZED, FAST_PTHREAD_ONCE_INIT будут просто проигнорированы, то есть некоторые 2 функции не будут вызваны ниразу несмотря на то, что вызывался once_call. |
||||||
|
|||||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |