1711
|
Qt / Многопоточное программирование, процессы / AtomicData или механизм атомарной операции записи данных
|
: Март 24, 2011, 03:48
|
Вечер добрый) Хочу спросить у бывалых в многопоточном программировании совета) Ситуация следующая: Имеется один поток, который пишет данные (в некотурую структуру) и есть несколько потоков, которые эти данные читают. Сами данные представляются в виде класса и ссылка на объект этого класса передаётся пишущему и читающим потокам. Ясно, что операция записи в общем случае не атомарна и могут возникнуть неприятности если один или более читающих потоков будут считывать даные, одновременно с записью их пишущим потоком. Использовать mutex тоже не очень рационально, поскольку доступ к данным для чтения будет доступен только одному потоку. Написать класс, который должен эту проблему решить, но есть сомнения... Вот сам класс: C++ (Qt) #ifndef ATOMICDATA_H #define ATOMICDATA_H template <class T> class AtomicData { public: AtomicData() : m_flag(0) {} AtomicData(const T &data) : m_flag(0), m_data(data) {} AtomicData(const AtomicData<T> &other) : m_flag(0), m_data(other.get()) {} void set(const T &data) { while (m_flag) {} m_flag = 1; m_data = data; m_flag = 0; } T get() const { while (m_flag & 1) {} m_flag = 2; T data = m_data; m_flag = 0; return data; } private: mutable volatile char m_flag; T m_data; AtomicData<T> &operator=(const AtomicData<T> &); }; #endif // ATOMICDATA_H Суть в том, что операция с char - атомарны (на сколько мне известно) и перед тем как считать или записать данные мы взводим соответствующий флаг m_flag. Значения: m_flag = 0 - никто не пишет, никто не читает; m_flag = 1 - кто-то пишет (читать запрещено). m_flag = 2 - кто-то читает (запись запрещена, читать можно). примечание: mutable - нужно чтоб метод get() можно было сделать const; volatile - чтоб компилятор не оптимизировал m_flag. Собственно вопрос: По фен-шую ли такое решение или есть грабли? Спасибо за внимание) P.S. пишу ядро и не хотелось бы завязывать его на сторонних библиотеках, Qt использую для ГУЯ только.
|
|
|
1715
|
Qt / Пользовательский интерфейс (GUI) / Re: Не работают сигналы и слоты
|
: Март 23, 2011, 14:23
|
У вас должно работать три экшена: 1) openAct 2) exitAct 3) aboutAct
Всё остальное делайте сами)
На Ubuntu действительно все работает, а на Seven x64 нет Что за хрень? На Семерке даже не запускается констуктор: MainWindow::MainWindow(QWidget *parent):QMainWindow(parent) { setupUi(this); createActions(); loadFile ("settings.ini"); } Нет причин, чтобы не работало под виндой.. Вы ничего не напутали случаем? Очистите весь проект и соберите заново.
|
|
|
1717
|
Qt / Пользовательский интерфейс (GUI) / Re: Не работают сигналы и слоты
|
: Март 23, 2011, 14:01
|
Подправил слегка ваш проект и всё заработало А теперь Вы нам скажите, что вы сделали не так и почему сейчас всё работает?)) Да и сейчас не работает, что я сейчас делаю не так? Могу скинуть экзешник... Да, а у меня работает У меня Linux, экзешник не запустится... Моментик, щас проверю ещё разок.
|
|
|
1719
|
Программирование / С/C++ / Re: тип size_t и сравнение указателей
|
: Март 21, 2011, 13:56
|
Да, хочу добавить вот что: Запустил Ваш тест в release и в debug версии со следующим изменением кода: C++ (Qt) void TestVirtual( const QVector <CBase *> & vec ) { QTime begT = QTime::currentTime(); int sum = 0; for (int i = 0; i < vec.size(); ++i) { CBase * base = vec[i]; for (size_t j = 0; j < NUM_CALL; ++j) sum += base->typeVirtual(); // Без изменений } int msecs = begT.msecsTo(QTime::currentTime()); qDebug() << "TestVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum; } void TestNonVirtual( const QVector <CBase *> & vec ) { QTime begT = QTime::currentTime(); int sum = 0; for (int i = 0; i < vec.size(); ++i) { CBase * base = vec[i]; for (size_t j = 0; j < NUM_CALL; ++j) { sum += base->typeNonVirtual(); // Для сравнения: оставим только вызов не виртуальной функции } } int msecs = begT.msecsTo(QTime::currentTime()); qDebug() << "TestNonVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum; }
Посмотрим, для начала, какую ошибку мы допускаем пренебрегая временем вызова не виртуальной функции. Т.е. сейчас мы сравниваем вирт. и не вирт. функции при прочих равных условиях. Результат (release): C++ (Qt) TestVirtual time(sec): 0.673 sum = 157286400 TestNonVirtual time(sec): 0 sum = 157286400
А в debug: C++ (Qt) TestVirtual time(sec): 1.529 sum = 157286400 TestNonVirtual time(sec): 0.995 sum = 157286400
Сравнивая, можно сделать вывод, что оптимизатор не так просто обмануть, во всяком случае на трюк с суммированием он не ведётся)) Теперь запомним это отношение: время вирт.ф / время не вирт. ф. = 1.529/0.995 = 1.54 И запустим в debug Ваш изначальный вариант: C++ (Qt) TestVirtual time(sec): 1.696 sum = 157286400 TestNonVirtual time(sec): 5.348 sum = 157286400
Теперь, вспоминая, что время TestNonVirtual нужно поделить на два: t = 5.348/2 = 2.674 и далее его ещё нужно поделить на отношение, которое было найдено выше, т.е. на 1.54: В результате имеем: C++ (Qt) TestVirtual time(sec): 1.696 TestNonVirtual time(sec): 1.736
Отношение этих значений есть: 1.736/1.696 =1.02 Что находится в хорошем согласии с моими результатами в debug версии.
|
|
|
1720
|
Программирование / С/C++ / Re: тип size_t и сравнение указателей
|
: Март 21, 2011, 12:52
|
И всё же я считаю, что Ваш тест не совсем чист: Во-первых, как Вы уже заметили, dynamic_cast в функции TestNonVirtual вызывается дважды. Во-вторых, C++ (Qt) void TestVirtual( const QVector <CBase *> & vec ) { QTime begT = QTime::currentTime(); int sum = 0; for (int i = 0; i < vec.size(); ++i) { CBase * base = vec[i]; for (size_t j = 0; j < NUM_CALL; ++j) sum += base->typeVirtual(); // Один вызов виртуальной функции } int msecs = begT.msecsTo(QTime::currentTime()); qDebug() << "TestVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum; } void TestNonVirtual( const QVector <CBase *> & vec ) { QTime begT = QTime::currentTime(); int sum = 0; for (int i = 0; i < vec.size(); ++i) { CBase * base = vec[i]; for (size_t j = 0; j < NUM_CALL; ++j) { CBaseA * a = dynamic_cast<CBaseA *> (base); // Раз: вызов dynamic_cast if (a) sum += a->typeNonVirtual(); // + Нужно учитывать дополнительное время от вызова обычной функции else { CBaseB * b = dynamic_cast<CBaseB *> (base); // Два: Вызов dynamic_cast if (b) sum += b->typeNonVirtual(); // Не учитываем добавочное время от этой операции? } } } int msecs = begT.msecsTo(QTime::currentTime()); qDebug() << "TestNonVirtual time(sec): " << (msecs / 1000.0f) << "sum =" << sum; }
В функции TestVirtual Виртуальная функция вызывается один (T_v - время вызова виртуальной функции) раз + время на суммирование (T_sum). Всё хорошо. В функции TestNonVirtual Дважды вызывается dynamic_cast (T_dyn - время вызова) + время вызова не виртуальной функции (T_nv) и + операция суммирования (T_sum). Я хочу сказать, что нужно сравнивать чистый вызов dynamic_cast и чистый вызов виртуальной функции. Зачем вообще все эти операции суммирования? Оптимизатор можно просто отключить) Например в debug версии. Кстати, в debug версии мой тест показывает следующие результаты (при трёх запусках): C++ (Qt) dynamic_cast: (1.56, 1.54, 1.54) sec; <t> = 1.547 sec virtual_fun: (1.49, 1.49, 1.48) sec; <t> = 1.487 sec
И получается, что виртуальная функция быстрее dynamic_cast (в данном конкретном случае) в 1.04 раз.
|
|
|
1721
|
Qt / Вопросы новичков / Re: Сигнал close
|
: Март 20, 2011, 23:05
|
Ой да ладно вам)) Какая мелочь, подумаешь не написал он этих функций) Компилятор и нормальная ID + Qt должны в таких ситуациях сами дописывать код) Живём в 21 веке и до сих пор приходится спрашивать с удивлением на форуме: а что это прога не компилится Безобразие, товарищи! Ну можно и без сарказма, все же я еще новичок Не, ну Вы так совсем обленитесь)) Надо хоть попытаться поискать ответ самому, прежде чем вопрошать к публике) Смысл тогда во всём?
|
|
|
1722
|
Программирование / С/C++ / Re: тип size_t и сравнение указателей
|
: Март 20, 2011, 22:41
|
Ну давайте немного поспорим. Можно покритиковать Ваш кусочек кода? Улыбающийся Вот прямо "близлежащий"
Ну я исчо учусь) Потом этот тест я написал на коленках за минут 10 чисто для себя, чтоб потом его нафиг удалить)) У меня ещё техника не доведена до такого автоматизма, чтоб так прям сразу, даже простой тест, писать идеологически правильно)) Логичнее критиковать то, откуда непосредственно ноги растут: libssc) Так что негусто ни с "уважением к читающему" ни с "архитектурой". Однако вынужден признать: с демагогией - все отлично Улыбающийся
Я исправлюсь
|
|
|
1723
|
Программирование / С/C++ / Re: тип size_t и сравнение указателей
|
: Март 20, 2011, 21:33
|
2 m_ax: попробуй увеличить иерархию наследования и рассматривать динамик-каст самого последнего потомка к самой первой базе.
Не спорю, вполне вероятно, что при этом ситуация может изменится, но мне как то фиолетово) У меня реально используется обин базовый класс и два наследуемые от него. Раньше я считал, что в моём случае (пример приведённый выше лишь отражает общую картину) dynamic_cast - очень медленная штука по сравнению с вызовом виртуальной функции, однако, как оказалось для беспокойства нет причин и я оставил вариант с dynamic_cast.
|
|
|
1724
|
Qt / Вопросы новичков / Re: Сигнал close
|
: Март 20, 2011, 21:18
|
Ой да ладно вам)) Какая мелочь, подумаешь не написал он этих функций) Компилятор и нормальная ID + Qt должны в таких ситуациях сами дописывать код) Живём в 21 веке и до сих пор приходится спрашивать с удивлением на форуме: а что это прога не компилится Безобразие, товарищи!
|
|
|
1725
|
Программирование / С/C++ / Re: тип size_t и сравнение указателей
|
: Март 20, 2011, 20:42
|
C++ (Qt) void test_virtual_fun(ListBase &l) { Iter it = l.begin(); for(; it != l.end(); ++it) { if ((*it)->type() == TYPE_F) { // Один вызов виртуального метода DerivedF *obj = static_cast<DerivedF*>(*it); // Пренебрегаем издержками на вызов static_cast obj->myFunc(); // один вызов не виртуального метода! } } }
На момент вызова myFunc() obj имеет тип DerivedF - значит никто не мешает компилятору вызвать DerivedF::myFunc() явно, минуя виртуальщину. Просто вызывайте (*it)->myFunc, пусть разбирается Да нет же, myFunc() - не виртуальная функция) Я бы мог её и не писать вообще, результат бы не изменился. И кстати, я не могу вызвать (*it)->muFunc() поскольку класс Base не имеет этого метода. Идея теста была сравнить что быстрей: dynamic_cast или вызов виртуальной функции. Conclusions: Хотя в данном случае dynamic_cast оказался быстрее, забивать голову и тратить время на подобные оптимизации - бредовая идея) Лучше потратить его (время) на обдумывание архитектуры и рассмотрения возможных вариантов её расширения в будущем) 4) Спорно/проблематично кто там больше "затуманивает". Часто текст превращается в демонстрацию знания C++ и автор озабочен не содержательной частью а лишь той самой "формальной грамотностью". Продравшись через все навороты обнаруживается, что дела-то "пшик", ф-циональность слаба. А бывает и наоборот
Формальная грамотность, формальная грамотность.. Грамотность при написании кода - это элементарное уважение к читающему код и залог безопасности (не маловажный факт). Понты, по мне - это когда в коде встречаются сплошь и рядом сомнительные хаки и прочие грабли, на которые разве что не наступит (и то сомнительно) сам автор, в то время, как истинная причина всего этого безобразия - плохая архитектура и чрезмерный С-консерватизм.
|
|
|
|
|