Russian Qt Forum
Май 05, 2024, 10:34 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало Форум WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  
  Просмотр сообщений
Страниц: 1 ... 112 113 [114] 115 116 ... 140
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;
 
 
Какой по вашему смысл в том, чтобы они были виртуальными? Чтобы в наследуемых классах каждый раз переопределять их одинаковым образом увеличивая при этом число строк кода и снижая производительность при их вызове? 
1702  Программирование / С/C++ / Re: Полиморфизм, наследование, проверка на принадлежность к группе классов. : Март 27, 2011, 15:55
Если заранее известно к чему нужно привести базовай класс, то dynamic_cast избыточен: как раз для этого предназначен static_cast.
static_cast ничего не проверит, он просто "приведет" если типы позволяют, NULL он не вернет
Согласен) В том то всё и дело, что проверка не нужна, если заранее известно к чему приводить)
Или мы о разном говорим?
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 удалить сам объект, скопировать указатель на новые данные и опять увеличить счётчик ссылок.
Наверное я чегото не допонимаю в этом, почему оно атомарно? 
увеличение числа ссылок, копирование указателя на объект
1707  Qt / Интернационализация, локализация / Re: Татарский перевод : Март 25, 2011, 16:19
Ох как я не любил татарский язык в школе)
Интересно, посмотреть на интерфейс программы на татарском))
И что за программа?
1708  Qt / Многопоточное программирование, процессы / Re: AtomicData или механизм атомарной операции записи данных : Март 25, 2011, 03:47
У меня ещё такой вопрос возник:
Является ли следующая операция атомарна? (m_flag - это char):
Код
C++ (Qt)
while (m_flag++);  // <<-- Атомарна ли оно?
critical_section();
m_flag = 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 механизма)
Спасибо за обсуждение, я ещё отпишусь, как возникнут вопросы)
Страниц: 1 ... 112 113 [114] 115 116 ... 140

Страница сгенерирована за 0.34 секунд. Запросов: 24.