1696
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 18:33
|
И так, вроде ничего не упустил) C++ (Qt) #ifndef ATOMICDATA_H #define ATOMICDATA_H #include <assert.h> #include <cstdatomic> template <class T> class AtomicData { public: typedef atomic<int> AtomicInt; AtomicData() : m_count_readers(0), m_state_write(-1) {} void setData(const T &data) { while (!acquireWrite(m_count_readers, m_state_write)); m_data = data; releaseWrite(m_state_write); } T getData() const { while (!acquireRead(m_count_readers, m_state_write)); T data = m_data; releaseRead(m_count_readers, m_state_write); return data; } private: AtomicInt m_count_readers; AtomicInt m_state_write; T m_data; static bool acquireWrite(AtomicInt &readers, AtomicInt &state_write) { bool tmp = (++state_write == 0); state_write = 0; return (tmp && !readers); } //------------------------------------------------------------------------- static void releaseWrite(AtomicInt &state_write) { state_write = -1; } //------------------------------------------------------------------------- static bool acquireRead(AtomicInt &readers, AtomicInt &state_write) { if (!readers) readers = (state_write == -1); else readers++; return readers; } //------------------------------------------------------------------------- static void releaseRead(AtomicInt &readers, AtomicInt &state_write) { if (--readers == 0) state_write = -1; } //------------------------------------------------------------------------- }; #endif // ATOMICDATA_H
Пояснения: m_counter_readers - число читающих потоков (в данный момент времени) m_state_write - если = -1 - никто не пишет, в противном случае есть ожидающие записи Читать данные могут одновременно несколько потоков, записывать всегда только какой-то один. Если кто-то пишет, читать запрещено.
|
|
|
1697
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 14:25
|
brankovic , Igors, Вынужден признать, что Вы правы)) Но уверен, решение близко и грех его не найти)) Если Вас не затруднит, не могли бы Вы дать хоть как-то осмысленные имена. Что такое key1, key2.- хз. "test_for" лучше заменить на "acquire", т.к. тестирование не имеет смысла при параллельном выполнении
Согласен, исправлю)
|
|
|
1698
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 12:49
|
C++ (Qt) static bool test_for_read(AtomicInt &key1, AtomicInt &key2) { bool res = (++key1 == 0); key2 = 0; key1 = -1; return res; } };
Грабли те же. Да, ++key1 атомарно, так что с того? В момент когда выполняется key2 = 0, др. нитка могла уже сделать с key1 все что угодно и возвращаемый res ничему не соответствует. Без "идиомы CAS" не обойтись. Просто "присваивание" (напр key1 = -1) почти всегда неверно (хоть и атомарно). И если уж взялись писать свой ReadWriteLocker, то делайте и "write pending" (запрос на запись) чтобы желающие, увидев этот флаг, не захватывали по чтению - иначе запись может не пробиться. Смотрите: Другая нитка конечно можнет сделать с key1 всё что угодно, но результат то уже будет другой) Например вначале key1 = -1. После первой проверки с инкрементом: C++ (Qt) res1 = (++key == 0) Мы получим res1 = true и key1 = 0; Далее другая нитка делает тоже, но key1 будет уже равен 1 и res вернёт false
|
|
|
1699
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 02:51
|
Вобщем найти этот atomic.h так и не удалось, но нашёл cstdatomic (c++0x) где реализован очень удобный шаблон atomic<T> C++ (Qt) #ifndef ATOMICDATA_H #define ATOMICDATA_H #include <cstdatomic> template <class T> class AtomicData { private: typedef atomic<int> AtomicInt; AtomicData() : m_key1(-1), m_key2(-1) {} void setData(const T &data) { while (!test_for_write(m_key1, m_key2)); // critical section m_data = data; m_key1 = -1; m_key2 = -1; } T getData() const { while (!test_for_read(m_key1, m_key2)); // critical section T res = m_data; m_key1 = -1; m_key2 = -1; return res; } private: AtomicInt m_key1; AtomicInt m_key2; T m_data; static bool test_for_write(AtomicInt &key1, AtomicInt &key2) { bool res1 = (++key1 == 0); bool res2 = (++key2 == 0); key1 = 0; key2 = 0; return (res1 && res2); } static bool test_for_read(AtomicInt &key1, AtomicInt &key2) { bool res = (++key1 == 0); key2 = 0; key1 = -1; return res; } };
|
|
|
1700
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 28, 2011, 01:33
|
Подправил изначальный вариант AtomicData. Теперь по идеи должно работать коррестно (пока правда только под linux). C++ (Qt) #ifndef ATOMICDATA_H #define ATOMICDATA_H #include <asm/atomic.h> template <class T> class AtomicData { private: AtomicData() { m_key1 = ATOMIC_INIT(-1); m_key2 = ATOMIC_INIT(-1); } void setData(const T &data) { while (!test_for_write(&m_key1, &m_key2)); // critical section m_data = data; atomic_set(&m_key1, -1); atomic_set(&m_key2, -1); } T getData() const { while (!test_for_read(&m_key1)); // critical section T res = m_data; atomic_set(&m_key1, -1); atomic_set(&m_key2, -1); return res; } private: atomic_t m_key1; atomic_t m_key2; T m_data; static bool test_for_write(atomic_t *key1, atomic_t *key2) { bool res1 = atomic_inc_and_test(key1); bool res2 = atomic_inc_and_test(key2); atomic_set(key1, 0); atomic_set(key2, 0); return (res1 && res2); } static bool test_for_read(atomic_t *key1) { bool res = atomic_inc_and_test(key1); atomic_set(&m_key2, 0); atomic_set(key1, -1); return res; } }; #endif // ATOMICDATA_H Где atomic_inc_and_test - атомарный инкремент и возвращает true если результат равен нулю. Флаги m_flag1 и m_flag2: Если оба равны -1 то никто не читает и никто не пишет (данные свободны) Если m_flag1 == -1 и m_flag2 != -1 то можно читать (более одного потока). Но возникла проблема: Нигде не могу найти у себя хедер atomic.h В каталоге asm его нет Где его искать?
|
|
|
1701
|
Программирование / С/C++ / Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
|
: Март 27, 2011, 16:15
|
m_ax, переменная _groupdatatypes вовсе не лишняя, она то мне и помогла разобраться с определением принадлежности класса к группе. А в классе NSimpleDataTypes определяется функция groupDataTypes, которая выдает значение той самой переменной. Т.е. _groupdatatypes инициализуемая в конструкторе указывает на то, что все наследуемые от NSimpleDataTypes производные классы будут принадлежать к eNSimpleDataTypes, с которыми программа работает, через интерфейсы определенным образом. Т.е. для это оказался самый практичный способ решения проблемы и к тому же удобный во многих отношениях, если заглядывать вперед.
Всё равно не понял) Чем, например это: C++ (Qt) NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; _groupdatatypes = eNSimpleDataTypes; } NGroupDataTypes groupDataTypes() const {return _groupdatatypes; } Отличается от: C++ (Qt) NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; } NGroupDataTypes groupDataTypes() const {return eNSimpleDataTypes; }
? И потом, надо помнить, что вызов виртуальной функции обходится дороже, вызова обычной функции и там где есть возможность обойтись без виртуальных функций лучше обходится без них. У вас же как раз такой случай в отношении 3 функций: C++ (Qt) virtual void setId(qint32 id) = 0; virtual QString name() const = 0; virtual void setName(QString name) = 0; Какой по вашему смысл в том, чтобы они были виртуальными? Чтобы в наследуемых классах каждый раз переопределять их одинаковым образом увеличивая при этом число строк кода и снижая производительность при их вызове?
|
|
|
1703
|
Программирование / С/C++ / Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
|
: Март 27, 2011, 15:06
|
Непонял: А тем, что мне надо проверить, а не пытаться привести в тип до проверки (так быстрее). Да и в программе, с использованием перечисления, код стал понятнее. А все dynamic_cast я выполняю только тогда, когда знаю к чему привести.
Если заранее известно к чему нужно привести базовай класс, то dynamic_cast избыточен: как раз для этого предназначен static_cast. И зачем в INDataTypes делать функции C++ (Qt) virtual qint32 id() const = 0; virtual void setId(qint32 id) = 0; virtual QString name() const = 0; virtual void setName(QString name) = 0;
виртуальными? Можно вполне сделать так: C++ (Qt) class INDataTypes { public: virtual ~INDataTypes() { } qint32 id() const { return _id; } void setId(qint32 id) { _id = id; } QString name() const { return _name; } void setName(QString name) { _name = name;} virtual NGroupDataTypes groupDataTypes() const = 0; // Оставить только эту фиртуальной protected: //если идентификатор равен -1, значит это либо временная переменная, либо константное значение qint32 _id; //константные или временные значения имен не имеют, только переменные. QString _name; };
Тогда, например в NSimpleDataTypes: C++ (Qt) class NSimpleDataTypes : public INSimpleDataTypes { public: NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; //_groupdatatypes = eNSimpleDataTypes; Нафига лишняя переменная? } NGroupDataTypes groupDataTypes() const {return eNSimpleDataTypes; /*_groupdatatypes; */ } /* Это тоже лишнее */ bool constValue() const {return _constValue;} void setConstValue(bool constValue) {_constValue = constValue;} };
|
|
|
1704
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 26, 2011, 20:27
|
Хорошо, тогда получается, что сама по себе операция копиррования и присваивания двух shared_ptr (я иммею в виду без участия DataManager) не является атомарной?
да И ещё вопрос: В классе DataManager используется shared_ptr только лишь как оптимизация? Ведь аналогичный эффект получится если просто использовать непосредственно Data?
да Как пример, такие DataManager-ы используется для классов хранения сеттингов (настройки программы), они могут быть довольно развесистыми. Рабочий тред пользуется сеттингами, но он 1. не должен их лочить, 2. должен видеть консистентное состояние настроек. Такая конструкция с shared_ptr позволяет ускорить захват сеттингов и тем самым минимизировать трение в локах. Но необходимости в shared_ptr здесь нет, просто он копируется на порядок быстрее, чем какой-нибудь std::map <std::string, std::vector <boost::any> >. Ясно Спасибо)
|
|
|
1705
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 26, 2011, 19:36
|
Только за счёт мьютекса, в данном примере. Дальше боюсь запутать, пытаясь домыслить неправильно поставленный вопрос.
Попробую я пояснить C++ (Qt) shared_ptr<Data> ptr; DataManager dataManager; ... for (int i = 0; i < 100 ++i) { ptr = dataManager.data(); ... }
Вполне возможно что напр после первых 10 итераций цикла др. нитка поставит др. data. В этом случае следующие итерации цикла будут работать уже с новыми данными. Это "атомарно" и будет работать корректно. Однако это совсем не значит что данные будут изменены для того кто их уже получил (до замены) с помощью data() - что получил, с тем и работает Да, это я понял) Всмысле, это вполне нормальное поведение (кто успел - тот съел ) А за счёт чего достигается атомарность копирования shared_ptr? Вот например: C++ (Qt) shared_ptr<Data> ptr; DataManager dataManager; ... ptr = dataManager.data();
При копировании, на сколько я понял должно произойти как минимум следующее: ptr - должен уменьшить свой счётчик ссылок и если он окажется 0 удалить сам объект, скопировать указатель на новые данные и опять увеличить счётчик ссылок. Наверное я чегото не допонимаю в этом, почему оно атомарно? Только за счёт мьютекса, в данном примере. Дальше боюсь запутать, пытаясь домыслить неправильно поставленный вопрос. Хорошо, тогда получается, что сама по себе операция копиррования и присваивания двух shared_ptr (я иммею в виду без участия DataManager) не является атомарной? А, если является, то это обеспечивается собственным мьютексом в самом shared_ptr? Тогда, если это так, то какой смысл в Read(Write)Locker в классе DataManager? И ещё вопрос: В классе DataManager используется shared_ptr только лишь как оптимизация? Ведь аналогичный эффект получится если просто использовать непосредственно Data?
|
|
|
1706
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 26, 2011, 14:20
|
Именно так все и делают, за счёт shared_ptr оверхеда здесь мизер. Собственно 2 атомарных операции на захват/отдачу мьютекса + 1 атомарная операция на копирование shared_ptr. В лучшем lockfree решении будет 2 атомарных операции. Только для этого lockfree не оправдано.
А за счёт чего достигается атомарность копирования shared_ptr? Вот например: C++ (Qt) shared_ptr<Data> ptr; DataManager dataManager; ... ptr = dataManager.data();
При копировании, на сколько я понял должно произойти как минимум следующее: ptr - должен уменьшить свой счётчик ссылок и если он окажется 0 удалить сам объект, скопировать указатель на новые данные и опять увеличить счётчик ссылок. Наверное я чегото не допонимаю в этом, почему оно атомарно? увеличение числа ссылок, копирование указателя на объект
|
|
|
1709
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 24, 2011, 21:16
|
Варианты
1) Сделать все самому. В нативных вызовах ничего особо страшного нет (OSSpinLockLock и InterlockedExchange для моих платформ, неплохо и QAtomicInt), но пройдя примерно половину пути приходишь к выводу - нерационально. Вот напр. у Вас пустое тело while. Эта, на первый взгляд несущественная мелочь, начинает доставлять неприятности - одно из ядер "загружено до упора" холостым ходом. Попытки решить это "сходу" (yield и.т.п) вызывают новые проблемы.
2) Использовать Qt QReadWriteLock. Здесь все прекрасно в том смысле что все решается очень быстро после чтения Assistant. Только одно маленькое "но" - Qt честно усыпляет нитки, поэтому при достаточно интенсивных блокировках скорость будет убита в ноль (N ядер медленнее одного).
3) После долгого обдумывания я выбрал Intel TBB и весьма доволен - шикарный выбор локов, atomic и scoped_lock.
А что Вы можете сказать о библиотеки ZThread? Судя по отзывам она неплоха)
|
|
|
1710
|
Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных
|
: Март 24, 2011, 15:06
|
Сомнения оправданы (: Посмотрите в сторону QReadWriteLock или погуглите lock-free алгоритмы ( http://www.1024cores.net/ http://www.rsdn.ru/forum/cpp/1800649.flat.aspx). Сценарий. Один поток исполняет инструкцию while (m_flag) {}, другой поток исполняет инструкцию while (m_flag & 1) {}, только потом оба потока устанавливают флаг, дальше непредвиденные последствия. Да, Вы правы, так и есть. На эти грабли будут наступать все и всегда, так что не расстраивайтесь Улыбающийся Дело в том что когда управление вернулось из while - все вроде Ок, m_flag равен нулю. Но беда в том что др. нитка может установить m_flag ПОСЛЕ while но ДО следующей команды (здесь m_flag = 1). Набор подобных ситуаций обсуждался http://www.prog.org.ru/index.php?topic=14448.msg94325#msg94325Вот вот, я уже понял как наступить на эти грабли) Вобщем сейчас пытаюсь разобраться с устройством lock-free механизма) Спасибо за обсуждение, я ещё отпишусь, как возникнут вопросы)
|
|
|
|
|