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

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Два потока работают с QQueue  (Прочитано 26224 раз)
Yegor
Гость
« : Октябрь 20, 2014, 16:54 »

Здравствуйте!

Работаю на Qt4.8.6

У меня есть два параллельных потока.

Стоит задача - передавать большое количество данных за единицу времени от одного потока к другому.
Данные разбиты на блоки. Одна такой блок - это структура. В структуре - байты.

Для этого можно использовать механизм обмена событиями между потоками. Но мне кажется, что это медленное решение,
так как сюда привлекаются отправка события, очереди события, прием из очереди и т.д.

Я думаю использовать вариант побыстрее. Сделать промежуточный буфер между этими потоками - QQueue. Один буфер общий на два потока. То есть один поток добавляет данные к очереди, а второй извлекает.

Подскажите, пожалуйста, какие могут быть подводные камни в таком решении. В доке написано, что QQueue - reentrant. То есть очередь защищена от проблем при использовании многопоточности. Но все, же, что еще нужно учитывать? Нужно ли делать синхронизацию между потоками?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #1 : Октябрь 20, 2014, 17:04 »

Нужно ли делать синхронизацию между потоками?
Для QQueue нужно обязательно.

В доке написано, что QQueue - reentrant. То есть очередь защищена от проблем при использовании многопоточности.
Нет, это значит, что методы можно повторно вызывать.
Записан
Yegor
Гость
« Ответ #2 : Октябрь 20, 2014, 19:25 »

А как синхронизировать? Приведите, пожалуйста, кусочки кода. Или сслыку.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #3 : Октябрь 20, 2014, 19:35 »

А как синхронизировать? Приведите, пожалуйста, кусочки кода. Или сслыку.
Поиск же по форуму... Улыбающийся
http://www.prog.org.ru/index.php?topic=14426.msg95463#msg95463
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Октябрь 20, 2014, 20:25 »

Для этого можно использовать механизм обмена событиями между потоками. Но мне кажется, что это медленное решение,
так как сюда привлекаются отправка события, очереди события, прием из очереди и т.д.

Я думаю использовать вариант побыстрее.
Все-таки стандартные средства (напр контейнер защищенный QReadWriteLock) позволяют сэкономить немало времени. Может стоит сначала проверить "насколько же они медленны"

Стоит задача - передавать большое количество данных за единицу времени от одного потока к другому.
Данные разбиты на блоки. Одна такой блок - это структура. В структуре - байты.
Тогда почему не lock-free? (т.е. без всяких контейнеров и мутексов).

Код
C++ (Qt)
QAtomicPointer <СData> theHead(0), theTail(0);
QAtomicInt theCount(0);
 
void PutData( CData * data )   // CData - Ваша структура
{
data->mNext = 0;
while (true) {
 CData * head = theHead;
 if (head && !theHead.testAndSetAcquire(head, 0)) continue;  // stop reading
 
 if (theCount++ == 0) {   // list is empty
   theHead = theTail = data;
   return;
 }
 
 while (true) {
   CData * tail = theTail;
   if (!tail || !theTail.testAndSetAcquire(tail, data)) continue;  // add data to list
   tail->mNext = data;
   break;
 }
 if (head) theHead = head;   // enable reading
 break;
}
}
 
CData * GetData( void )
{
while (theCount) {
 CData * cur = theHead;
 if (!cur) continue;   // wait for writer
 if (!theHead.testAndSetAcquire(cur, cur->mNext)) continue;
 --theCount;
 return cur;
}
return 0;
}
 
Впрочем синхронизация все равно нужна. А главное - это капитально "выносит моск", напр я совсем не уверен что написал правильно. Ну если конечно Вы не боитесь трудностей...  Улыбающийся
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #5 : Октябрь 20, 2014, 21:53 »

Как-то вариант, предложенный Old проще для восприятия. Тоже недавно интересовался подобным вопросом.
Не совсем разобрался в эффективности применения QAtomic
Записан
vulko
Гость
« Ответ #6 : Октябрь 21, 2014, 08:38 »

Никакие атомарные операции тут не нужны. В 99.99% случаев вполне достаточно обычного мьютекса или крит. секции.

Передавать данные между потоками ненужно, лучше данные пошарить.
Создайте разделяемый ресурс.
Например пусть это будет класс.

Код:
class ShareData {

public:
    ShareData() { myData = new char[1024]; }
   
    void lock() { mLock.lock(); }
    void unlock() { mLock.unlock(); }

    void setData(char* data) {
        lock();
        // copy from data
        memcpy(data, myData, 1024);
        unlock();
    }
   
    void getData(char* data) {
        lock();
        // copy to data
        memcpy(myData, data, 1024);
        unlock();
    }

private:
    boost::mutex mLock; // можно использовать std::mutex, или QMutex
    char* myData;

}

Когда один поток обращается к данным, например для записи, обрамляете этот код вызовами
shareData->lock();
// синхронизируемый код
shareData->unlock();

Когда другой поток обращается к данным, например для чтения, также обрамляете код вызовами
shareData->lock();
// синхронизируемый код
shareData->unlock();


Если данных много и они должны обрабатываться постоянно, то можно сделать контейнер для хранения общих данных. Например вектор, а не char*, как в примере выше.
Тогда если !vector.isEmpty(), значит можно данные забирать и обрабатывать. Если нет, то ничего делать получающему потоку не нужно.
А передающий поток будет просто делать vector.push_back(item);
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #7 : Октябрь 21, 2014, 08:49 »

Никакие атомарные операции тут не нужны. В 99.99% случаев вполне достаточно обычного мьютекса или крит. секции.
А в каких случаях целесообразно применять атомарные операции?
Записан
vulko
Гость
« Ответ #8 : Октябрь 21, 2014, 09:20 »

Никакие атомарные операции тут не нужны. В 99.99% случаев вполне достаточно обычного мьютекса или крит. секции.
А в каких случаях целесообразно применять атомарные операции?

Когда хотите изобрести свою синхронизацию.

lock/unlock мьютекса это как раз атомарные операции.
Записан
OKTA
Гость
« Ответ #9 : Октябрь 21, 2014, 09:37 »

А можно QSemaphore c фабрикой попробовать. разве нет?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Октябрь 21, 2014, 09:48 »

А в каких случаях целесообразно применять атомарные операции?
В общем случае когда время выполнения "того что надо сделать под локом" достаточно мало. Напр то же добавление/извлечение из контейнера. Если такие операции интенсивны, то прямолинейное использование "честного" мутекса (который останавливает нитку) будет печально по скорости. Можно использовать нечестный (unfair), это просто
Код
C++ (Qt)
struct CAtomMutex {
void Lock( void )
{
  while (!mVal.testAndSetAcquire(0, 1))
    QThread::yieldCurrentThread();
}
 
void Unlock( void )
{
  mVal = 0;
}
 
QAtomicInt mVal;
};

А как синхронизировать? Приведите, пожалуйста, кусочки кода. Или сслыку.
Возможно и "никак", тупо ждем пока откроется мутекс. В этом есть смысл если по задаче мы знаем что время ожидания достаточно мало (ориентир < 0.3 sec)
Записан
OKTA
Гость
« Ответ #11 : Октябрь 21, 2014, 09:58 »

Интересная статья по поводу QAtomic http://woboq.com/blog/introduction-to-lockfree-programming.html
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Октябрь 21, 2014, 10:41 »

Интересная статья по поводу QAtomic http://woboq.com/blog/introduction-to-lockfree-programming.html
Да что ж он, гад, делает! Дает код, показывает что в 10 раз быстрее, а потом (так, между прочим, в конце статьи) говорит что там баг, и как фиксить хз. Он же меня запутал!!! Улыбающийся

Да, если интересно почему баг (там не очень ясно почему) - я поясню, это заморочка капитальная. А в общем ну его нафиг, тот lock-free, достигнутая чудесная скорость не окупает мозговых затрат на нее.
Записан
OKTA
Гость
« Ответ #13 : Октябрь 21, 2014, 10:58 »

Зато повод узнать о существовании QAtomic и его возможностях  Улыбающийся
Записан
vulko
Гость
« Ответ #14 : Октябрь 21, 2014, 11:44 »

А в каких случаях целесообразно применять атомарные операции?
В общем случае когда время выполнения "того что надо сделать под локом" достаточно мало. Напр то же добавление/извлечение из контейнера. Если такие операции интенсивны, то прямолинейное использование "честного" мутекса (который останавливает нитку) будет печально по скорости. Можно использовать нечестный (unfair), это просто

Не путай людей своей бредятиной.
lock() и unlock() это атомарные операции.

Применение или не применение их никак не связано с временем выполнения кода, который между ними.

Целесообразность определяется не тем сколько времени выполняется код под локом, важна архитектура и потребности.

Прямолинейное использование это что такое вообще? О каких честных/нечестных мьютексах вообще речь, когда ты не понимаешь как они работают даже.

Обрамлять локом/анлоком мьютекса операции вставки/удаления в контейнер крайне глупо.
Во-первых, для таких случаев используется критическая секция, т.к. она работает куда быстрее.
Во-вторых, удаление обрамлять локом анлок вообще очень глупо, особенно если нужно пробежаться по контейнеру и поудалять элементы... или удалить все по очереди.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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