Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: labview от Июль 30, 2010, 21:52



Название: Очереди, самодельные сигналы
Отправлено: labview от Июль 30, 2010, 21:52
Привет!

Я, как программист на LabVIEW предпочитаю многопоточное программирование. Этот язык программирования отличается тем, что в нём автоматически создаются параллельные потоки, если нет прямой связи между функциями. То есть защищаться нужно от так называемых Race Conditions, а не бороться с трудностями параллельного программирования циклов. Короче наоборот, чем в C++.

Так вот, мне бы хотелось узнать какие возможности предлагает Qt для многопоточного программирования. Особенно меня интерессуют очереди для обмена данными между потоками. То что есть семафоры я уже узнал. В принципе очередь можно заменить глобальной или локальной переменной, добавив семафор.

Спасибо!


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 02, 2010, 23:23
Самодельные сигналы можно делать с помощью emit, я правильно понимаю?

Вопрос с очередями (или FIFO) всё же остаётся.


Название: Re: Очереди, самодельные сигналы
Отправлено: SimpleSunny от Август 02, 2010, 23:45
Да, правильно, свои сигналы "запускают" по emit.

А что с очередями не понятно? Можно, к примеру, в главном потоке создать класс хранилище, а потокам отдавать ссылку на него.
Код
C++ (Qt)
class storage
{
   QReadWriteLock lock;
   QList <int> list;
public:
   int getLast() {lock.lockForRead();i = list.last(); lock.unlock(); return i;}
   void addLast(int i) {lock.lockForWrite(); list.push_back(i); lock.unlock();}
};

Или можно соединить потоки через "сигнал-слот" для обмена данными.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 03, 2010, 00:01
QList видимо хорошая штука, прочтал немного про него, а так же нашёл QQueue. Видимо как раз то, что мне нужно.
Так же уже нарыл инфу о Mutex.

Буду пробовать, спасибо!


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 03, 2010, 12:30
А что с очередями не понятно? Можно, к примеру, в главном потоке создать класс хранилище, а потокам отдавать ссылку на него.
Код
C++ (Qt)
class storage
{
   QReadWriteLock lock;
   QList <int> list;
public:
   int getLast() {lock.lockForRead();i = list.last(); lock.unlock(); return i;}
   void addLast(int i) {lock.lockForWrite(); list.push_back(i); lock.unlock();}
};
Все верно, но, к сожалению, при интенсивном чтении такого QList 4 нитки работают медленнее чем 1  :'(


Название: Re: Очереди, самодельные сигналы
Отправлено: SimpleSunny от Август 03, 2010, 14:10
А за счет чего, в данном случае, получается такие тормоза?
С высоконагруженным паралельным программированием не работал.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 03, 2010, 14:39
Спасибо за ответы.

Меня собственно интересуют следующие функции/возможности межпоточного синхронизированного обмена информацией без потерь данных:

1. Создание FIFO, где можно указать тип элемента и максимальное количество элементов, с поддержкой безграничного количества элементов.

2. Запись элемента в конец FIFO из любого из потоков. Если FIFO переполнено, то поток ждёт (застревает без пожирания CPU) пока в FIFO не появится место для элемента.

3. Предпросмотр первого элемента FIFO, без вытаскивания его из FIFO.

4. Доступ к информации о количестве элементов в FIFO, а так же ко всем элементам в FIFO в виде массива, без вытаскивания их из FIFO.

5. Закрытие FIFO, при котором все элементы уничтожаются, референс на FIFO становится недействительным. Если какой то поток находится в ждущем режиме IDLE (хочет считать элемент), то функция считывания прекращает своё действие и возвращает ошибку, т.к. референс на FIFO более недействителен.

6. Запись элемента в конец FIFO, при этом отличие от пункта 2, то, что если FIFO переполнено, то первый элемент удаляется.

7. Запись элемента в начало FIFO (то есть это уже будет LIFO).

8. Чтение первого элемента FIFO. При этом, если в FIFO нет элементов, то эта функция ждёт (находится в режиме IDLE, без пожирания CPU) пока элемент не появится. Так же здесь должен быть параметр Timeout, который говорит о том, как долго функция должна оставаться в состоянии IDLE. По прохождении времени Timeout, функция возвращает ошибку.

9. Удаление всех элементов из FIFO, без уничтожения самой FIFO. Т.е. референс остаётся действительным. Эта функция возвращает все уничтоженные элементы очереди в виде массива.


Поскажите пожалуста каким способом это лучше всего реализовать, может быть уже существуют подобные реализации.

Ещё раз огромное спасибо.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 03, 2010, 15:06
А за счет чего, в данном случае, получается такие тормоза?
QMutex, QReadWriteLock используют ОС реализацию mutex, которая может работать очень медленно при наличии интенсивной конкуренции за ресурс. Для примера OpenMP имеет настраиваемый параметр - время прокрутки (холостого хода) нитки перед тем как ее усыпить. По умолчанию это значение 200 ms - то есть 0.2 секунды. Делайте выводы


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 03, 2010, 15:19
Меня собственно интересуют следующие функции/возможности межпоточного синхронизированного обмена информацией без потерь данных:
Если говорить о структуре данных, то для всего что Вы перечислили ниже, QList прекрасно подходит т.к. он оптимизирован для вставок/удаления элементов в голову и в хвост. Также важно при вставке/удалении в QList адрес других элементов не меняется.

Как организовать синхронизацию между нитками - ну здесь "волшебной палочки" не существует. Разумно начать как показал SimpleSunny. Потом, может быть, использовать простейший атомарный лок - для этого достаточно будет только заменить QReadWriteLock на др. класс. А может и так оставить. В общем решать задачи в порядке их поступления.


Название: Re: Очереди, самодельные сигналы
Отправлено: lit-uriy от Август 03, 2010, 16:19
одно предостережение, у QList есть ограничение на размер (ограничен индексом типа int), он не безграничен


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 15:22
Привет!

Пытаюсь разобраться. Немного не понимаю как в контрукторе нити добавить другие параметры кроме name. Может кто нибудь помочь?
Ну скажем например какое нибудь число n.

Спасибо, код с комментариями прилагаю.

Код:
#include <iostream>
#include <QThread>
#include <QList>
#include <QReadWriteLock>

//Дефинируем класс состоящий из FIFO + lock для межпоточного обмена данными
class MyQueue
{
    QReadWriteLock lock;
    QList <int> list;
public:
    int i;
    int getLast() {lock.lockForRead();i = list.last(); lock.unlock(); return i;}
    void addLast(int i) {lock.lockForWrite(); list.push_back(i); lock.unlock();}
};

//Дефинируем класс MyThread полученный из QThread
class MyThread : public QThread {
public:
  MyThread( std::string a = "MyThread" );
  MyQueue myQueue; //внутри нити создаём нашу очередь
private:
  std::string name;
  virtual void run();
};

//Как я понимаю это конструктор
MyThread::MyThread( std::string a ) : name(a)
{}

//Перезагружаем функцию run() тем, что нить должна делать после запуска
void MyThread::run()
{
    int  b = 1;
    myQueue.addLast(b);
    b = myQueue.getLast();
    std::cout << name <<  b  << std::endl;
}


int main()
{
  MyThread a("A");
  MyThread b("B");
  a.start();
  b.start();
  a.wait();
  b.wait();
}


Название: Re: Очереди, самодельные сигналы
Отправлено: kuzulis от Август 04, 2010, 15:32
Как вариант. В конструкторе потока должен быть добавлен указатель на объект типа MyQueue.
Тогда несколько потоков смогут иметь доступ к MyQueue.

Код:
class MyThread : public QThread {

    Q_OBJECT

public:
  MyThread(MyQueue *queue, QObject *parent = 0);
private:
  MyQueue *myQueue;
protected:
    void run();
};

Код:
MyThread::MyThread(MyQueue *queue, QObject *parent)
        : QThread(parent), myQueue(queue)
{
}

Код:
void MyThread::run()
{
    int  b = 1;
    myQueue->addLast(b);
    b = myQueue->getLast();
    std::cout <<  b << std::endl;
}

Код:
int main()
{
  MyQueue mq;
  MyThread a(&mq);
  MyThread b(&mq);
  a.start();
  b.start();
  a.wait();
  b.wait();
}


Название: Re: Очереди, самодельные сигналы
Отправлено: SimpleSunny от Август 04, 2010, 15:44
Ваш код содержит потенциальную ошибку.
Код
C++ (Qt)
int i;
int getLast() {lock.lockForRead();i = list.last(); lock.unlock(); return i;}
void addLast()...
Допустим есть 3 потока: 2 читают (1й и 2й), 1 (3й) пишет.
1й вызывает getLast() и перед return i переходит переключение на 3й поток, тот добавляет какое-то значение в конец, переключение на 2й поток, который успевает сделать i = list.last(), переключение на 1й поток. Дальше все зависит от положение солнца и влажности воздуха.

Как добавлять параметры в конструкторе показали, в принципе, выше.
Можно также сделать отдельный метод для передачи параметров (setParameter(int)), если параметров много, можно завернуть их в отдельную структуру \ клас.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 18:57
Дело в том, что у каждого потока будет иметься своя собственная очередь. То есть два читателя одной очереди исключаются.


Название: Re: Очереди, самодельные сигналы
Отправлено: SimpleSunny от Август 04, 2010, 19:25
Дело в том, что у каждого потока будет иметься своя собственная очередь. То есть два читателя одной очереди исключаются.

Если у каждого потока своя очередь, тогда зачем междупотоковая синхронизация (QReadWriteLock)?


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 19:42
Вот именно, что незачем. Видимо я что то попутал. Мне кажется, мне нужен сигнал, а не семафор.

На данный момент мои попытки изучить использование выглядят так:

Код:
#include <iostream>
#include <QThread>
#include <QList>
#include <QMutex>

class MyQueue
{
    QMutex lock;
    QList <int> list;
public:
    int i;
    int getLast() {lock.lock(); i = list.last(); lock.unlock(); return i;}
    void addLast(int i) {lock.lock(); list.push_back(i); lock.unlock();}
};

class WriterThread : public QThread {

public:
  WriterThread(MyQueue *queue, QObject *parent = 0);
private:
  MyQueue *myQueue;
protected:
  void run();
};

WriterThread::WriterThread(MyQueue *queue, QObject *parent)
        : QThread(parent), myQueue(queue)
{
}

void WriterThread::run()
{
    for(int  i = 0; i <= 100; i++)
    {
        myQueue->addLast(i);
        this->msleep(100);
    }
}

class ReaderThread : public QThread {

public:
  ReaderThread(MyQueue *queue, QObject *parent = 0);
private:
  MyQueue *myQueue;
protected:
  void run();
};

ReaderThread::ReaderThread(MyQueue *queue, QObject *parent)
        : QThread(parent), myQueue(queue)
{
}

void ReaderThread::run()
{
    int  i = 0;
    while(i < 100)
    {
        i = myQueue->getLast();
        std::cout <<  i  << std::endl;
    }
}

int main()
{
  MyQueue mq;
  WriterThread a(&mq);
  ReaderThread b(&mq);
  a.start();
  b.start();
  a.wait();
  b.wait();
}

Мне просто нужен сигнал (event) о том, что первый поток вложил в очередь данные, вот и всё.
Каким способом это реализовать?


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 20:11
Извините, может быть я неправильно выразился.

В общем мне нужна система сигналов и слотов, но с доработками.
- Сигналы должны буферизоваться, пока не будут обработаны (наверное так оно и есть уже в Qt)
- Размер буфера для сигналов хотелось бы задавать самому
- При переполнении размера буфера с сигналами должна быть возможность удалить первый и в конец буфера добавить новый
- Так же хотелось бы опустошить буфер с сигналами, так и не обработав их

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


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 04, 2010, 20:42
А нужно ли Вам в читающей нитке брать run на себя? Пока Вы имеете от этого одни неудобства. Напр. что должен вернуть getLast если список пуст? Как организовать ожидание если данных для обработки пока нет? Воэиться с этим оправдано только если нужно выжать скорость. Иначе намного проще посылать этой нитке сигналы (коннект с флагом QueuedConnection). По умолчанию run вызовет exec, сигналы будут приниматься

Локи все равно нужны если данные общие для 2 и более ниток. Выглядеть может примерно так (не проверял, ошибки возможны)

Код
C++ (Qt)
#include <iostream>
#include <QThread>
#include <QList>
#include <QMutex>
 
class MyQueue
{
   QMutex mutex;
   QList <int> list;
public:
   bool getLast( int & i )
   {
      QMutexLocker lock(&mutex);
      if (!list.size()) return false;
      i = list.last();
      return true;
   }
 
   void addLast( int i )
   {
      QMutexLocker lock(&mutex);
      list.push_back(i);
   }
};
 
class WriterThread : public QThread {
 
public:
 WriterThread(MyQueue *queue, QObject *parent = 0);
signals:
 void NewData( void );  
 void EndJob  ( void );  
private:
 MyQueue *myQueue;
protected:
 void run();
};
 
WriterThread::WriterThread(MyQueue *queue, QObject *parent)
       : QThread(parent), myQueue(queue)
{
}
 
void WriterThread::run()
{
   for(int  i = 0; i <= 100; i++)
   {
       myQueue->addLast(i);
       emit NewData();
       this->msleep(100);
   }
   emit EndJob();
}
 
class ReaderThread : public QThread {
 
public:
 ReaderThread(MyQueue *queue, QObject *parent = 0);
public slots:
 void NewData( void );
private:
 MyQueue *myQueue;
};
 
ReaderThread::ReaderThread(MyQueue *queue, QObject *parent)
       : QThread(parent), myQueue(queue)
{
}
 
void ReaderThread::NewData( void )
{
   int i;
   if (myQueue->getLast(i))
     std::cout <<  i  << std::endl;
}
 
int main()
{
 MyQueue mq;
 WriterThread a(&mq);
 ReaderThread b(&mq);
 QObject::connect(&a, SIGNAL(NewData()), &b, SLOT(NewData()), Qt::QueuedConnection);
 QObject::connect(&a, SIGNAL(EndJob()), &b, SLOT(quit()), Qt::QueuedConnection);
 a.start();
 b.start();
 a.wait();
 b.wait();
}


Название: Re: Очереди, самодельные сигналы
Отправлено: SimpleSunny от Август 04, 2010, 21:04
Если задания только раздаются и обрабатываются только в принимающем потоке и соединение типа "много писателей, один выполняющий", то можно попробовать вообще без локов.

Код
C++ (Qt)
#include <QThread>
#include <QList>
 
class WriterThread : public QThread
{
Q_OBJECT
 
public:
 WriterThread(QObject *parent = 0) : QThread(parent) {};
 
signals:
 void NewData(int);
 
protected:
 void run();
};
 
void WriterThread::run()
{
   for(int  i = 0; i <= 100; ++i)
       emit NewData(i);
}
 
class ReaderThread : public QThread
{
public:
 ReaderThread(QObject *parent = 0) : QThread(parent) {};
 
public slots:
 void NewData(int i);
 
private:
 QList <int> list;
};
 
void ReaderThread::NewData(int i)
{
   //Тут обрабатывать очередь
   list.push_back(i);
   if (list.size > listMaxSize)
       list.removeFirst();
   //и т. д. и т. п.
}
 
int main()
{
 WriterThread a();
 ReaderThread b();
 connect(&a, SIGNAL(NewData(int)), &b, SLOT(NewData(int)), Qt::QueuedConnection);
 a.start();
 b.start();
 a.wait();
 b.wait();
}
 


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 21:17
Спасибо конечно, но не один из последних примеров не компилируется. В примере Igors пишет, что connect не декларирован в main.

А если добавляю "QObject::", то пишет undefined reference to WriterThread::NewData() и undefined reference to WriterThread::EndJob().

Буду разбираться.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 04, 2010, 21:19
QObject::connect  :)


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 22:18
Не компилируется, я что то ещё забыл?

Цитировать
debug/main.o: In function `ZN12WriterThreadC2EP7MyQueueP7QObject':
D:/C++/QThread-build-desktop/../QThread/main.cpp:43: undefined reference to `vtable for WriterThread'
debug/main.o: In function `ZN12WriterThreadC1EP7MyQueueP7QObject':
D:/C++/QThread-build-desktop/../QThread/main.cpp:43: undefined reference to `vtable for WriterThread'
debug/main.o: In function `ZN12ReaderThreadC2EP7MyQueueP7QObject':
D:/C++/QThread-build-desktop/../QThread/main.cpp:71: undefined reference to `vtable for ReaderThread'
debug/main.o: In function `ZN12ReaderThreadC1EP7MyQueueP7QObject':
D:/C++/QThread-build-desktop/../QThread/main.cpp:71: undefined reference to `vtable for ReaderThread'
debug/main.o: In function `ZNK15QBasicAtomicIntneEi':
c:/Qt/4.6.3/include/QtCore/../../src/corelib/tools/qlist.h:(.text$_ZN12WriterThreadD1Ev[WriterThread::~WriterThread()]+0xb): undefined reference to `vtable for WriterThread'
c:/Qt/4.6.3/include/QtCore/../../src/corelib/tools/qlist.h:(.text$_ZN12ReaderThreadD1Ev[ReaderThread::~ReaderThread()]+0xb): undefined reference to `vtable for ReaderThread'
collect2: ld returned 1 exit status
mingw32-make.exe[1]: *** [debug/QThread.exe] Error 1
C:\MinGW\bin\mingw32-make.exe: *** [debug] Error 2


Название: Re: Очереди, самодельные сигналы
Отправлено: SimpleSunny от Август 04, 2010, 22:51
макрос Q_OBJECT прописали?


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 04, 2010, 23:08
Именно поэтому и перестало компилироваться.
Но ладно, я потом уже раскидал дефинирование классов по хедерам, вроде стало лучше, потом появилось сообщение, что EventLoop не работает без QApplication. Оказалось что проект был откунфигурирован под консоль. Переконфигурировал проект, стало компилироваться и выполняться. Но ничего к сожалению не выдаётся.
Сейчас сижу дибагю, не понимаю почему сигнал не emit-ится.

Код:
#ifndef MYQUEUE_H
#define MYQUEUE_H

#include <QList>
#include <QMutex>

class MyQueue
{
    QMutex mutex;
    QList <int> list;
public:
    bool getLast( int & i )
    {
       QMutexLocker lock(&mutex);
       if (!list.size()) return false;
       i = list.last();
       return true;
    }

    void addLast( int i )
    {
       QMutexLocker lock(&mutex);
       list.push_back(i);
    }
};

#endif // MYQUEUE_H

Код:
#ifndef READERTHREAD_H
#define READERTHREAD_H

#include <QThread>
#include "MyQueue.h"

class ReaderThread : public QThread {

Q_OBJECT

public:
  ReaderThread(MyQueue *queue, QObject *parent = 0);
public slots:
  void NewData( void );
private:
  MyQueue *myQueue;
};

#endif // READERTHREAD_H

Код:
#ifndef WRITERTHREAD_H
#define WRITERTHREAD_H

#include <QThread>
#include "MyQueue.h"

class WriterThread : public QThread {

Q_OBJECT

public:
  WriterThread(MyQueue *queue, QObject *parent = 0);
signals:
  void NewData( void );
  void EndJob  ( void );
private:
  MyQueue *myQueue;
protected:
  void run();
};

#endif // WRITERTHREAD_H

Код:
#include <iostream>
#include "QApplication.h"
#include "WriterThread.h"
#include "ReaderThread.h"

WriterThread::WriterThread(MyQueue *queue, QObject *parent)
        : QThread(parent), myQueue(queue)
{
}

void WriterThread::run()
{
    for(int  i = 0; i <= 100; i++)
    {
        myQueue->addLast(i);
        emit NewData();
        this->msleep(100);
    }
    emit EndJob();
}

ReaderThread::ReaderThread(MyQueue *queue, QObject *parent)
        : QThread(parent), myQueue(queue)
{
}

void ReaderThread::NewData( void )
{
    int i;
    if (myQueue->getLast(i))
      std::cout <<  i  << std::endl;
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  MyQueue mq;
  WriterThread a(&mq);
  ReaderThread b(&mq);
  QObject::connect(&a, SIGNAL(NewData()), &b, SLOT(NewData()), Qt::QueuedConnection);
  QObject::connect(&a, SIGNAL(EndJob()), &b, SLOT(quit()), Qt::QueuedConnection);
  a.start();
  b.start();
  a.wait();
  b.wait();
  return app.exec();
}


Название: Re: Очереди, самодельные сигналы
Отправлено: ufna от Август 04, 2010, 23:35
После того, как добавил этот макрос (а его обязательно добавь, без него сигналы и т.п. работать не будут), сделай clean project, затем qmake, а потом только - сборку проекта. Второй шаг обязателен, т.к. сий глюк втаблов оттуда именно.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 05, 2010, 01:53
Привет!

Мне кажется, я взял задание, которое на моей стадии познания Qt и C++ мне непосильно. Для начала разберусь с системой сигналов и слотов для обмена данными между потоками. А потом, когда мне будет всё до конца ясно, возьмусь за реализацию своей идеи.

Всем на этом огромное спасибо.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 12, 2010, 02:05
Скажите а какой нибудь функции с примерно таким названием "bool WaitOnEvent(event *event, int timeout)" в Qt не существует? Ну чтобы поток замирал на время Timeout, при условии если ивент не приходит? Но если ивент приходит, то сразу же реагировал, т.е. заканчивал выполнение этой функции с возвращением 0.


Название: Re: Очереди, самодельные сигналы
Отправлено: galilley от Август 12, 2010, 06:58
Как-то мне надо было реализовать блокирующий read данных из одного потока в другой, чтобы первичный поток засыпал пока функция не будет обработана. Суть была в том, что я из первичого потока вызывал метод объекта, находящегося в основном потоке, там бросал event тому объекту, что я хочу читать, и чтобы он отправил сигнал вторичному потоку, и локал первичный поток семафором. После того, как приходил сигнал с данными от вторичного потока я кидал их в переменную, разблокировал симафор и данные возвращались в первичный поток return-ом.
Может Вам может помочь какая-нить подобная реализация? Или есть методы проще, а я много намутил...


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 12:05
Самый простой способ: запустить нитку, она начнет крутить  свой eventLoop. Когда для нитки есть работа - послать ей сигнал. Нитка выполнит задание, вернет результаты (также через сигнал) и опять уйдет в eventLoop. Для этого в Qt все заточено, дополнительные семафоры не нужны. Просто создавайте и связывайте слоты/сигналы


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 12, 2010, 12:37
Но насколько я понял event loop всегда относится к главному потоку и в Qthread не поддерживается.
Или я ошибаюсь?


Название: Re: Очереди, самодельные сигналы
Отправлено: galilley от Август 12, 2010, 12:45
Самый простой способ: запустить нитку, она начнет крутить  свой eventLoop. Когда для нитки есть работа - послать ей сигнал. Нитка выполнит задание, вернет результаты (также через сигнал) и опять уйдет в eventLoop. Для этого в Qt все заточено, дополнительные семафоры не нужны. Просто создавайте и связывайте слоты/сигналы

Это всё понятно, но мне как раз нужда была синхронность, чтобы я мог, например, послать задание во вторичный поток, потом ещё что-нить сделать по ходу в первичном, потом дождаться ответа и выполнять программу дальше.



Название: Re: Очереди, самодельные сигналы
Отправлено: Авварон от Август 12, 2010, 12:48
это асинхронность...


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 12:53
Но насколько я понял event loop всегда относится к главному потоку и в Qthread не поддерживается.
Или я ошибаюсь?
Ошибаетесь. Метод exec() (он стоит в run() по умолчанию) запускает eventLoop этой нитки


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 13:02
Это всё понятно, но мне как раз нужда была синхронность, чтобы я мог, например, послать задание во вторичный поток, потом ещё что-нить сделать по ходу в первичном, потом дождаться ответа и выполнять программу дальше.
А Вас никто не заставляет ждать пока задание выполнится. Делайте коннект QueuedConnection и управление вернется после того как задание помещено в очередь нитки. А хотите ждать - используйте BlockingQueuedConnection


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 12, 2010, 13:04
Но насколько я понял event loop всегда относится к главному потоку и в Qthread не поддерживается.
Или я ошибаюсь?
Ошибаетесь. Метод exec() (он стоит в run() по умолчанию) запускает eventLoop этой нитки

Да, я попутал, где то прочитал, что в Qt за User Interface всегда отвечает Main, и спутал это с за Event Loop всегда отвечает Main.
То есть в нитях нельзя создавать свои пользовательские окна.

Да, я согласен с тем, что система сигналов и слотов реализована достаточно хорошо. Но мне бы хотелось иметь прямой доступ к Thread, а не через его Event Loop, понимаете?
Ещё мне бы хотелось, раз уж такой возможности нет?!, сбрасывать сигналы (то есть делать их недействительными), если нить их например не может обработать. А так же иметь возможность указать максимальное количество сигналов (размер буфера) для Event Loop определённой нити. И ещё менять сигналы местами или добавлять сигналы не в конец буфера, а в начало например.

Блин, во время написания этого ответа вспомнился момент из камеди клаба:
http://video.google.com/videoplay?docid=4145626155888365960#


Название: Re: Очереди, самодельные сигналы
Отправлено: galilley от Август 12, 2010, 13:30
А Вас никто не заставляет ждать пока задание выполнится. Делайте коннект QueuedConnection и управление вернется после того как задание помещено в очередь нитки. А хотите ждать - используйте BlockingQueuedConnection

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

Да, я согласен с тем, что система сигналов и слотов реализована достаточно хорошо. Но мне бы хотелось иметь прямой доступ к Thread, а не через его Event Loop, понимаете?
Ещё мне бы хотелось, раз уж такой возможности нет?!, сбрасывать сигналы (то есть делать их недействительными), если нить их например не может обработать. А так же иметь возможность указать максимальное количество сигналов (размер буфера) для Event Loop определённой нити. И ещё менять сигналы местами или добавлять сигналы не в конец буфера, а в начало например.
А что за задача, если не секрет? Может там надо решать проблему другими способами, или с другого угла.. да и крайне интересны условия, в которых могли возникнуть такие потребности.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 13:50
Но я хотел бы ещё ту же получить ответ из вторичного треда.. хотя да, ответ можно спросить и напрямую, дождавшись пока отработает BlockingQueuedConnection.
Случай когда надо ждать ответа - довольно редкий. В большинстве случаев ответ посылается из нитки выполнившей задание (тоже сигналом) и принимается в eventLoop главной (или др) нитки.  Др. словами гораздо удобнее "ждать с пользой" в eventLoop


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 14:00
IMHO стоит взглянуть на QRunnable + QThreadPool.
eventloop в простаивающей нитке все равно кушает процессор и чем больше таких ниток, тем больше кушает.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 12, 2010, 14:10
А что за задача, если не секрет? Может там надо решать проблему другими способами, или с другого угла.. да и крайне интересны условия, в которых могли возникнуть такие потребности.

Ну во первых я не очень понимаю зачем каждой нити свой Event Loop, то есть каждая нить состоит из двух потоков - Event Loop + сам цикл нити. Наверняка как то можно передавать данные напрямую и синхронно в цикл нити, не используя Event Loop.

Во вторых нить например должна отправлять данные какому нибудь внешнему прибору. Допустим данные имеют разный приоритет. Для примера команда exit имеет высший приоритет, а если в данный момент в буфере Event Loop уже находятся другие команды низкого приоритета, то мне бы хотелось убрать команды низкого приоритета из буфера сигналов и сразу выполнить сигнал высокого приоритета. Ну или просто всунуть команду высокого приоритета в буфер на первое место, а остальные подождут.

Ну примерно понятно?


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 14:17
Сделать свою очередь заданий будет самое то.
Кстати на форуме были темы, где выкладывались готовые рабочие примеры организации таких задач.
Так же можно посмотреть реализацию QRunnable и QThreadPool.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 14:26
Да, я согласен с тем, что система сигналов и слотов реализована достаточно хорошо. Но мне бы хотелось иметь прямой доступ к Thread, а не через его Event Loop, понимаете?
Думаю что понимаю :) Но, судя по тому что Вы рассказали, никакой необходимости в этом не видно
Ещё мне бы хотелось, раз уж такой возможности нет?!, сбрасывать сигналы (то есть делать их недействительными), если нить их например не может обработать. А так же иметь возможность указать максимальное количество сигналов (размер буфера) для Event Loop определённой нити. И ещё менять сигналы местами или добавлять сигналы не в конец буфера, а в начало например.
Мне кажется Вы путаете синхронное с асинхронным  :) Сигнал всего лишь "посылает нитке задание". На посылающей стороне как правило нет выбора - задачи посылаются исполняющей нитке в порядке их поступления. Но исполнитель совсем не обязан следовать тому же порядку. В любой момент он может вызвать processEvents и посмотреть какие еще задачи пришли, прервать выполнение текущей задачи, установить свой порядок выполнения задач и.т.п.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 14:33
eventloop в простаивающей нитке все равно кушает процессор и чем больше таких ниток, тем больше кушает.
Вряд ли. Запускаем любую Qt программу которая открывает UI и ждет ввода пользователя (т.е. стоит на eventLoop). Активность процессора (ов) на нуле.


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 14:38
Вряд ли. Запускаем любую Qt программу которая открывает UI и ждет ввода пользователя (т.е. стоит на eventLoop). Активность процессора (ов) на нуле.
Что значит вряд ли.  :) Если посмотреть реализацию, то на каждой итерации, помимо обработки самих событий, происходит еще масса разных действий. И все это будет выполняться во всех таких нитках без всякой нужды.



Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 14:45
Ну во первых я не очень понимаю зачем каждой нити свой Event Loop, то есть каждая нить состоит из двух потоков - Event Loop + сам цикл нити. Наверняка как то можно передавать данные напрямую и синхронно в цикл нити, не используя Event Loop.
Когда ОС запускает нитку, дается ф-ция которую эта нитка начнет выполнять. Как только ф-ция вернет управление - нитка завершена. Qt по умолчанию запускает eventLoop. Вы можете вместо этого сделать свой цикл нитки (обычно не очень удобно), можете войти в eventLoop, выйти, опять войти - как угодно но все в пределах run() нитки.

Стандартная проблема с собственным циклом - нитке нужно ждать пока данных для обработки нет и нужно как-то сниматься с ожидания когда данные пришли. Во многих случаях выходит то же самое что делает eventLoop


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 14:51
Стандартная проблема с собственным циклом - нитке нужно ждать пока данных для обработки нет и нужно как-то сниматься с ожидания когда данные пришли. Во многих случаях выходит то же самое что делает eventLoop
А в чем эта проблема?
Нитка останавливается на объекте QWaitCondition::wait, когда задание добавляется, то делаем QWaitCondition::wakeOne и одна из ниток просыпается и подхватывает задание.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 14:52
Что значит вряд ли.  :) Если посмотреть реализацию, то на каждой итерации, помимо обработки самих событий, происходит еще масса разных действий. И все это будет выполняться во всех таких нитках без всякой нужды.
Согласен, реализация через eventLoop скоростью не блещет. Напр. если каждая задача - обработка 1 точки имеджа, то все уйдет в eventLoop. Но такие скоростные задачи редки, напр. для обработки изображения построчно eventLoop.вполне годится.


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 14:59
Согласен, реализация через eventLoop скоростью не блещет. Напр. если каждая задача - обработка 1 точки имеджа, то все уйдет в eventLoop. Но такие скоростные задачи редки, напр. для обработки изображения построчно eventLoop.вполне годится.
Конечно, он вполне годиться для задач время выполнения которых значительно больше времени выполнения итерации eventloop. Просто потому, что последним временем можно пренебречь.  :)
Но для чего его вообще крутить? Если можно написать свое более эффективное решение, к тому же labview нужны хитрая очередь с приоритетами.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 15:11
А в чем эта проблема?
Нитка останавливается на объекте QWaitCondition::wait, когда задание добавляется, то делаем QWaitCondition::wakeOne и одна из ниток просыпается и подхватывает задание.
Это "выглядит просто"  :) Когда вызывается QWaitCondition::wakeOne - нужно быть уверенным что кто-то уже спит на QWaitCondition::wait, а определить это может быть нелегко


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 15:14
Это "выглядит просто"  :)
Да нет, это и реализуется просто.  :)

Когда вызывается QWaitCondition::wakeOne - нужно быть уверенным что кто-то уже спит на QWaitCondition::wait, а определить это может быть нелегко
А для чего?


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 15:43
Когда вызывается QWaitCondition::wakeOne - нужно быть уверенным что кто-то уже спит на QWaitCondition::wait, а определить это может быть нелегко
А для чего?
Есть задание, данные для нитки-обработчика записаны, вызываем wakeOne. Однако обработчик еще занят предыдущим запросом и wakeOne не имеет эффекта. Обработка закончена, нитка честно ждет на wait, но поезд уже ушел.

Но для чего его вообще крутить? Если можно написать свое более эффективное решение, к тому же labview нужны хитрая очередь с приоритетами.
Очередь все равно формируется обработчиком, сценарий один и тот же, 1 виток цикла выглядит так:

- извлечь все пришедшие сигналы/события  из очереди (удобно из eventLoop)
- поместить все задания во внутреннюю очередь, там рассортировать как надо, удалить ненужные
- заняться первым заданием

Можно сделать и прерывание текущей задачи - ведь пришедшие сигналы доступны в любой момент. Зачем же городить свою внешнюю очередь вместо eventLoop ?


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 12, 2010, 15:47
Есть задание, данные для нитки-обработчика записаны, вызываем wakeOne. Однако обработчик еще занят предыдущим запросом и wakeOne не имеет эффекта. Обработка закончена, нитка честно ждет на wait, но поезд уже ушел.
Ну это как написать.  ;)

Очередь все равно формируется обработчиком, сценарий один и тот же, 1 виток цикла выглядит так:

- извлечь все пришедшие сигналы/события  из очереди (удобно из eventLoop)
- поместить все задания во внутреннюю очередь, там рассортировать как надо, удалить ненужные
- заняться первым заданием

Можно сделать и прерывание текущей задачи - ведь пришедшие сигналы доступны в любой момент. Зачем же городить свою внешнюю очередь вместо eventLoop ?
Вот это и называется городить огород.  ;)
Кстати, а как могут быть доступны сигналы, если уже запущена задача (долгий цикл) и обработка событий в связи с этим приостановлена?


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 12, 2010, 16:01
Кстати, а как могут быть доступны сигналы, если уже запущена задача (долгий цикл) и обработка событий в связи с этим приостановлена?
С помощью вставки в долгие циклы банального processEvents, которая вызовет получение всех пришедших сигналов. Просто когда сигнал получен - не бросаться его обрабатывать


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 12, 2010, 16:16
Думаю нашёл что искал:

QWaitCondition::wait
QWaitCondition::wakeOne

Самое то!!! Спасибо, осталось с ними разобраться и всё должно получится, как я хотел.

Буду использовать QList + QWaitCondition вместо сигналов и слотов для коммникации между потоками.

Мне кажется Вы путаете синхронное с асинхронным  :) Сигнал всего лишь "посылает нитке задание". На посылающей стороне как правило нет выбора - задачи посылаются исполняющей нитке в порядке их поступления. Но исполнитель совсем не обязан следовать тому же порядку. В любой момент он может вызвать processEvents и посмотреть какие еще задачи пришли, прервать выполнение текущей задачи, установить свой порядок выполнения задач и.т.п.
Думаю не путаю, если получатель будет следовать определённым правилам и всегда исполнять сигналы быстрее чем отправитель их отправляет, то синхронность обеспечивается. Единственная незначительная задержка это переключение между потоками во время отправления сигнала, которая обусловлена осью.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 13, 2010, 10:32
Скажите пожалуйста это правда что event loop неглавного потока выполняется в главном потоке, а только сам цикл, находящийся в Run() выполняется параллельно главному?


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 13, 2010, 10:57
Скажите пожалуйста это правда что event loop неглавного потока выполняется в главном потоке, а только сам цикл, находящийся в Run() выполняется параллельно главному?
eventloop это и есть сам цикл и работает он в отдельном потоке.
Очередь сообщений тоже создается своя для каждого потока.

[off]Но вопрос зачОтный, читал несколько раз, много думал. :)[/off]


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 13, 2010, 11:18
Спасибо, не понял  ;D

Event Loop один на все QThreads, но очередь ивентов для каждого разная?
Или у каждого Qthread свой Event Loop и естесственно своя очередь ивентов?

Если я нажимаю на кнопку на интерфейсе и вызываю слот, который был определён в одном из QThread, то функция в слоте выполняется в том же QThread или в другом, одним на все QThread?


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 13, 2010, 11:22
Event Loop один на все QThreads, но очередь ивентов для каждого разная?
Или у каждого Qthread свой Event Loop и естесственно своя очередь ивентов?
Каждая нитка выполняет свой цикл и очередь сообщений у каждого своя.

Если я нажимаю на кнопку на интерфейсе и вызываю слот, который был определён в одном из QThread, то функция в слоте выполняется в том же QThread или в другом, одним на все QThread?
Если объекты находятся в разных потоках (посылающий и принимающий) и используется не DirectConnection, то слот будет отрабатывать в контексте принимающего потока.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 13, 2010, 11:25
Ага, теперь понял, большое спс  ;)


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 13, 2010, 14:17
Каждая нитка выполняет свой цикл и очередь сообщений у каждого своя.
Да, но нужно дополнить: любая нитка (кроме главной) должна вызвать exec(), иначе никакого eventLoop у нее не будет и никаких сообщений/сигналов она принять не сможет.


Название: Re: Очереди, самодельные сигналы
Отправлено: lit-uriy от Август 13, 2010, 17:19
labview, перефразирую то, что сказал Igors
event loop - цикл обработки событий - это метод exec().


Цитировать
любая нитка (кроме главной)
и главная тоже, если она не вызывает exec(), то и у неё не будет цикла обработки событий


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 13, 2010, 17:36
Цитировать
любая нитка (кроме главной)
и главная тоже, если она не вызывает exec(), то и у неё не будет цикла обработки событий
Ну главная нитка не QThread и ее нельзя создать/удалить, но в принципе правильно: без QApplication::exec ничего не получится


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 17, 2010, 20:01
Привет!

Думаю я уже близок к реализации моей задумки. По каким то причинам программа зависает. Скажите пожалуйста, что я неправильно сделал?

И так задумка (чтобы не читать весь топик).

Eсть два потока. Для начала один записывает один считывает данные. Данные между потоками передаются не через сигналы и слоты, а через Qlist с использованием QWaitCondition. Должно по-моему выпоняться так, что стартуются два потока, второй ждёт кондиции и спит в это время. Первый при старте записывает в Qlist данные и будит второй поток. Тот считывает эти данные, когда его разбудили.

Систему QList+QWaitCondition я назвал Tasking (т.к. логически передаются задания с параметрами из одного потока во второй).

И так Tasking.h

Код:
#ifndef TASKING_H
#define TASKING_H

#include <QList>
#include <QWaitCondition>
#include <QMutex>
#include <QString>

struct task
{
    int tasknum;
    QString data;
};


class Tasking
{
private:
    QList<task> *taskList;
    QWaitCondition *cond;
    QMutex *mutex;
public:
    Tasking();
    ~Tasking();
    task getTask();
    void addTask(task newTask);
};

#endif // TASKING_H

Tasking.cpp

Код:
#include "Tasking.h"

Tasking::Tasking()
{
    mutex = new QMutex;
    taskList = new QList<task>;
    cond = new QWaitCondition;
}

Tasking::~Tasking()
{

}

task Tasking::getTask()
{
    if(!(taskList->count()==0))
    {
        mutex->lock();
        cond->wait(mutex, ULONG_MAX);
        mutex->unlock();
    }

    return taskList->last();
}

void Tasking::addTask(task newTask)
{
    cond->wakeOne();
    taskList->push_back(newTask);
}

Thread1 (который посылает задание второму)

Код:
#include "thread1.h"


Thread1::Thread1(Tasking *allTasks[], int tasksCount)
{
    Tasks = new Tasking * [tasksCount];
    for(int i = 0; i < tasksCount; i++)
    Tasks[i] = allTasks[i];
}

void Thread1::run()
{
    task myTask;
    myTask.tasknum = 1;
    myTask.data = "something";
    Tasks[0]->addTask(myTask);
    emit showData(myTask.data);
}

Thread2 (который ждёт задания)

Код:
#include "thread2.h"

Thread2::Thread2(Tasking *allTasks[], int tasksCount)
{
    Tasks = new Tasking * [tasksCount];
    for(int i = 0; i < tasksCount; i++)
    Tasks[i] = allTasks[i];
}

void Thread2::run()
{
    task myTask;
    myTask = Tasks[0]->getTask();
    emit showData(myTask.data);
}


Main

Код:
#include <QtGui/QApplication>

#include "mainwidget.h"
#include "Tasking.h"
#include "thread1.h"
#include "thread2.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;
    w.show();

    Tasking *allTasks[1];

    Tasking *tasking1 = new Tasking;
    allTasks[0] = tasking1;
    Thread1 myThread1(allTasks, sizeof(allTasks)/sizeof(allTasks[0]));
    Thread2 myThread2(allTasks, sizeof(allTasks)/sizeof(allTasks[0]));

    QObject::connect(&myThread1, SIGNAL(showData(QString)), &w, SLOT(addText(QString)));
    QObject::connect(&myThread2, SIGNAL(showData(QString)), &w, SLOT(addText(QString)));

    myThread1.start();
    myThread2.start();
    myThread1.wait();
    myThread2.wait();

    return a.exec();
}

Спасибо!


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 17, 2010, 20:55
Думаю я уже близок к реализации моей задумки. По каким то причинам программа зависает. Скажите пожалуйста, что я неправильно сделал?
Да много чего  :)

1)
Код:
if(!(taskList->count()==0))
    {
        mutex->lock();
        cond->wait(mutex, ULONG_MAX);
        mutex->unlock();
    }
То есть если список НЕ пустой. то ждем? Отрицание надо убрать

2) Если QList используется 2 или более нитками и есть хотя бы 1 операция записи в него, то все обращения к такому QList (как по записи так и по чтению) должны быть защищены (напр мутексом) как уже обсуждали в этой теме

3) Если Вы хотите делать свой цикл нитки - делайте, напр так
Код
C++ (Qt)
void Thread2::run( void )
{
  while (!mStop) {
    ...
  }
}
 
А то у Вас в run по 1 задаче стоит

4)
Должно по-моему выпоняться так, что стартуются два потока, второй ждёт кондиции и спит в это время. Первый при старте записывает в Qlist данные и будит второй поток.
Во-первых, в коде наоборот, сначала будит, потом записывает
Код:
void Tasking::addTask(task newTask)
{
    cond->wakeOne();
    taskList->push_back(newTask);
}

А во-вторых, очень важно понять: при параллельном выполнении нет никаких гарантий что операция в 1 нитке выполнится до или после операции во 2 нитке. Это непредсказуемо. В данном случае никто не обещает что "нитка 2" заснет до ее побудки ниткой 1. Ничего не изменится даже если Вы запустите нитку 2 первой.

Если Вы хотите класть задания в очередь и вынимать из нее, то есть простой и хороший класс QSemaphore с методами acquire и release. Разница в том что не нужно заморачиваться с выяснением "а спит ли уже нитка?". Добавили в очередь и вызвали release семафора. Если нитка 2 стоит на acquire, она разблокируется. А если нет - не беда, тогда не будет ждать на acquire когда до этого дойдет дело.


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 17, 2010, 20:57
Набросаю свои мысли.

Код
C++ (Qt)
class Tasking
{
private:
// Не знаю, для чего использовать здесь указатели, поэтому предлагаю сделать проще:
QList<task> taskList;
QMutex mutex;
QWaitCondition cond;
 
public:
task getTask();
void addTask(task newTask);
};
 

Код
C++ (Qt)
task Tasking::getTask()
{
QMutexLocker locker( &mutex );
while( taskList.empty() )
cond.wait( &mutex );
 
return taskList.takeFirst();
}
 
void Tasking::addTask(task newTask)
{
QMutexLocker locker( &mutex );
taskList.append( newTask );
cond.wakeOne();
}
 


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 17, 2010, 21:07
Код
C++ (Qt)
task Tasking::getTask()
{
QMutexLocker locker( &mutex );
while( taskList.empty() )
cond.wait( &mutex );
 
return taskList.takeFirst();
}
 
void Tasking::addTask(task newTask)
{
QMutexLocker locker( &mutex );
taskList.append( newTask );
cond.wakeOne();
}
 
Если getTask захватила мутекс и лист пуст - имеем dead lock. С семафором получается прекрасно

Код
C++ (Qt)
task Tasking::getTask()
{
semaphore.acquire();
QMutexLocker locker( &mutex );
return taskList.takeFirst();
}
 
void Tasking::addTask(task newTask)
{
QMutexLocker locker( &mutex );
taskList.append( newTask );
semaphore.release();
}
 


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 17, 2010, 21:09
Если getTask захватила мутекс и лист пуст - имеем dead lock.
Да ну. А может стоит разобраться с работой wait condition.  ;)


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 17, 2010, 21:14
Спасибо ребята!!! Подправил ошибки, работает!

На счёт цикла в потоке я знаю конечно, пока что делал для примера без цикла. Гляньте плиз на счёт мутекса, думаю теперь всё в порядке, или?

Да, на счёт того, какой поток стартонёт первым, думаю здесь мне не нужно знать спит ли второй поток или нет, ведь если допустим первый стартует быстрее и вписывает в лист задание и будит второй, а второй ещё не стартонулся и не спит, то ведь он и не засыпает, если задание в листе имеется.

Код:
    if(taskList.count()==0)
    {
        cond.wait(&mutex, ULONG_MAX);
    }



Код:
#include "Tasking.h"

Tasking::Tasking()
{
}

Tasking::~Tasking()
{

}

task Tasking::getTask()
{
    if(taskList.count()==0)
    {
        cond.wait(&mutex, ULONG_MAX);
    }

    mutex.lock();
    task currentTask = taskList.last();
    mutex.unlock();

    return currentTask;
}

void Tasking::addTask(task newTask)
{
    mutex.lock();
    taskList.push_back(newTask);
    mutex.unlock();
    cond.wakeOne();
}


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 17, 2010, 21:21
Хм.
Я не зря написал петлю, она нужна, так как в некоторых случаях поток может проснутся не вовремя (1).
Причем, этот код должен быть уже залочен мьютексом (2).
Код
C++ (Qt)
       QMutexLocker locker( &mutex ); // <<<<<<<<<<< 2
       while( taskList.empty() ) // <<<<<<<<<<< 1
               cond.wait( &mutex );
 
       return taskList.takeFirst();
 


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 18, 2010, 09:30
Гляньте плиз на счёт мутекса, думаю теперь всё в порядке, или?
"Или"  :)  Мутекс должен быть захвачен до вызова wait, поэтому правильно так
Код:
task Tasking::getTask()
{
    mutex.lock();
    if(taskList.count()==0)
        cond.wait(&mutex, ULONG_MAX);

    task currentTask = taskList.last();
    mutex.unlock();

    return currentTask;
}
wait освободит мутекс перед остановом нитки, а когда нитка снимется с ожидание - снова захватит


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 18, 2010, 09:55
wait освободит мутекс перед остановом нитки, а когда нитка снимется с ожидание - снова захватит
Точно.  :)

Только цикл еще нужен, проверяющий count() и выполняющий wait().  ;)


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 18, 2010, 10:14
Да, на счёт того, какой поток стартонёт первым, думаю здесь мне не нужно знать спит ли второй поток или нет, ведь если допустим первый стартует быстрее и вписывает в лист задание и будит второй, а второй ещё не стартонулся и не спит, то ведь он и не засыпает, если задание в листе имеется.
Это если Вы берете задание и сразу удаляете его из очереди (как в варианте BRE). А если не удаляете (как у Вас) то "лист пустой или нет" ни о чем не говорит

Только цикл еще нужен, проверяющий count() и выполняющий wait().  ;)
Нет, цикл не нужен (хотя он ничего и не портит)


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 18, 2010, 10:35
Нет, цикл не нужен (хотя он ничего и не портит)
Цикл нужен обязательно, если конечно нет желания отлавливать не системные ошибки связанные с блокировками некоторых потоков в произвольный момент времени.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 18, 2010, 12:58
Цикл нужен обязательно, если конечно нет желания отлавливать не системные ошибки связанные с блокировками некоторых потоков в произвольный момент времени.
Может Вас смущает это (QWaitCondition::wait)
Код
C++ (Qt)
mutax->unlock();
 
bool returnValue = d->wait(time);
 
Да, др. нитка может захватить освобожденный мутекс, но она не прорвется на wakeOne. т.к. будет остановлена на другом, внутреннем мутексе QWaitCondition (d->mutex). То есть pthread_cond_signal придет после pthread_cond_wait


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 18, 2010, 13:05
Может Вас смущает это (QWaitCondition::wait)
Нет, меня смущает это:
Код
C++ (Qt)
   if(taskList.count()==0)
       cond.wait(&mutex, ULONG_MAX);
 

вместо этого:
Код
C++ (Qt)
   while( taskList.count()==0 )
       cond.wait(&mutex, ULONG_MAX);
 

И я писал выше почему.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 18, 2010, 13:17
И я писал выше почему.
Выше я вижу
Цикл нужен обязательно, если конечно нет желания отлавливать не системные ошибки связанные с блокировками некоторых потоков в произвольный момент времени.
Весьма мутное/общее "почему"  :) Поясните что то за  "не системные ошибки", "некоторые потоки" и "моменты времени"


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 18, 2010, 13:26
Я не зря написал петлю, она нужна, так как в некоторых случаях поток может проснутся не вовремя...
wait не гарантирует, что проснется только один поток.
В очередь поместили одно задание, 5 потоков ждут на wait. Планировщик может разбудить не один поток, а например 2 (точнее все которые стоят в очереди).
Первый вынимает данные из очереди, следом пробуждается второй поток, для него данных уже нет. Но он проснулся и полез в пустую очередь.
А вот эта петля, как раз уберегает поток от такого поведения, поток просто проверит, что очередь пуста и уснет на wait снова.

На самом деле, мы сейчас обсуждаем настолько примитивные вещи, которых нет пожалуй только в азбуке.
Еще раз порекомендую:
Цитировать
А может стоит разобраться с работой wait condition.
Только основательно.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 18, 2010, 16:16
Может быть я не очень ясно обьяснил идею. У каждого потока свой лист, свой мютекс, свой вэйт кондишн. Будиться будет только один определённый поток, если имеено в его лист пришло задание.

При этом задания будут приходить (добавляться в лист) из разных потоков, а исполнительный поток выбирается индексаторм. Таким образом любой поток может посылать задания любому другому потоку (через глобальный массив Tasking и индексацию массива).


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 18, 2010, 16:19
На самом деле, мы сейчас обсуждаем настолько примитивные вещи, которых нет пожалуй только в азбуке.
Еще раз порекомендую:
Цитировать
А может стоит разобраться с работой wait condition.
Только основательно.
Давайте  основательно :)

Я не зря написал петлю, она нужна, так как в некоторых случаях поток может проснутся не вовремя...
wait не гарантирует, что проснется только один поток.
В очередь поместили одно задание, 5 потоков ждут на wait. Планировщик может разбудить не один поток, а например 2 (точнее все которые стоят в очереди).
Первый вынимает данные из очереди, следом пробуждается второй поток, для него данных уже нет. Но он проснулся и полез в пустую очередь.
А вот эта петля, как раз уберегает поток от такого поведения, поток просто проверит, что очередь пуста и уснет на wait снова.
Это так для pthread_cond_signal, который будит "хотя бы 1 нитку" (но может и несколько). Но я вижу что QWaitCondition.wakeOne будит одну (и только одну) нитку (как и сказано в Assistant). Пробуждение остальных пресекается  т.к.  d->wakeups становится равным нулю.

Ну и не мешало бы сразу пояснить что эти проблемы связаны с 2 и более нитками ждущими на 1 QWaitCondition, а то "петля-петля"  :)


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 18, 2010, 19:46
2 Igors
Я абсолютно серьезно рад тому, что ты основательно разобрался с работой wait condition. Без обид.
Но кроме Qt далеко не все библиотеки (про нативные функции я вообще молчу) занимаются такими чудесами, а это именно чудеса, т.к. большинство специалистов прекрасно знают эти моменты и знают возможности их обхода. Любой программист начавший свою работу с потоками и их синхронизацией используя Qt про эти моменты никогда не узнает, до тех пор пока ему не придется использовать нативные функции или тот-же boost (или в Qt 4.8 решат отказаться от таких чудес?). А после это у этого программиста наступит ступор, потому что у него на Qt аналогичный код работал, а сейчас без Qt - нет. Именно для этого я несколько раз указывал про этот момент с петлей и напишу еще раз:
Специальное не использование петли в этой ситуации это попытка нажить себе большие не приятности с трудно обнаруживаемыми ошибками.
Ты с этим не согласен? Для чего этот спор?


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 18, 2010, 22:24
Разрешите ещё раз повториться и узнать. Каждому потоку присваевается свой личный объект QWaitCondition, так сделать можно?
И ещё какая принципиальная разница между использованием QWaitCondition и QSemaphore в данном контексте?

Спасибо.


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 19, 2010, 08:52
Каждому потоку присваевается свой личный объект QWaitCondition, так сделать можно?
Объект QWaitCondition связан больше не с потоком, а с данными (очередью) и сигнализирует возможен или нет доступ к ним.
Можно сделать, что бы у каждого потока будет своя защищенная очередь и эти очереди никак не будут пересекаться.
Расскажи подробней, что ты хочешь сделать.
Как я понял, ты хочешь запустить по своему потоку для каждой задачи. А что если в процессе выполнения одна задачу будет выполняться значительно чаще других, то потоки запущенные для других задач будут простаивать? Думаю можно будет обойти данную неприятность, только нужно больше информации по задаче.

И ещё какая принципиальная разница между использованием QWaitCondition и QSemaphore в данном контексте?
У семафоров немного другое предназначение: позволить доступ к ресурсу заданному количеству клиентов, если клиентов больше заданного количества, остальные будут ждать.
При желании можно задать количество клиентов == 1.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 19, 2010, 10:32
Ты с этим не согласен? Для чего этот спор?
Да конечно согласен, если бы Вы сразу пояснили - и не пытался бы спорить  :) Сам я с мутексами не работаю, нужна скорость, поэтому только OpenMP и atomic

Разрешите ещё раз повториться и узнать. Каждому потоку присваевается свой личный объект QWaitCondition, так сделать можно? И ещё какая принципиальная разница между использованием QWaitCondition и QSemaphore в данном контексте?
Можно и личный объект, но не видно зачем - ведь тогда придется решать какую нитку будить.

Семафор не то чтобы "лучше", но проще в использовании. Заметьте что QWaitCondition нужна "подпорка" (в данном случае проверка "пуста ли очередь"), без этого QWaitCondition не пляшет. А у семафора этой проблемы нет. Оба класса используют примерно одинаковый механизм реализованный на тех же ф-циях ОС, но скорость семафора вдвое выше (хотя это существенно для немногих задач). Можно сказать что QWaitCondition это семафор, обернутый в еще 1 мутекс.

Попробуйте сделать на 1 семафоре (просто 1 глобальный объект QSemaphore) - Вам понравится  :)


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 19, 2010, 10:59
Да конечно согласен, если бы Вы сразу пояснили - и не пытался бы спорить  :) Сам я с мутексами не работаю, нужна скорость, поэтому только OpenMP и atomic
Честно говоря, я все это писал для labview и людей которые будут это читать позже, которым возможно придется использовать низкоуровневые средства синхронизации. И специально для этого отметил, что петля в данном случае необходима. Кстати, у labview не возникло дополнительных вопросов по этому поводу, но прозвучало твое заявление насчет того, что это все не нужно. Спорить начал ты, вместо того чтобы просто спросить о назначение этой петли. И цель этого мне не понятна.
Хотя судя по всей теме прогресс конечно есть, было узнано:
* как же на самом деле работают условные переменные;
* для чего нужна петля;
* про чудеса внутри QWaitCondition.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 14:47
Давайте я приведу (выдуманый мной) пример.
Задание: имеются пять внешних приборов, подключеных (допустим) по последовательному интерфейсу к компьютеру: спидометр(датчик скорости), компас (датчик направления), GPS антенна (датчик глобальных координат), руль (актор направления) и педаль газа (актор скорости). Нужно собрать информацию с датчиков, высчитать оптимальное направление и скорость, показать пользователю и передать на акторы (руль и педаль газа). Так же имеется файл, в котором построчно записана траектория движения в виде глобальных координат.
Чтобы не запутаться в программе, нужно логическое разделение программы на модули, к тому же нужно считывать данные асинхронно (т.к. допустим частота передачи данных у всех датчиков отличается) и независимо друг от друга.

Идея состоит в том, чтобы каждый прибор был логически и практически отделён друг от друга. И так каждый модуль состоит из отдельного класса-обьекта, который существует в своём независимом потоке и программно отображает реальный обьект. У каждого такого модуля есть различные задания (tasks). Для межпоточной коммуникации я бы хотел использовать не сигналы и слоты, а лист (буфер) заданий.

Поясняю почему. Во-первых (здесь я могу ошибаться) сигналы и слоты обрабатываются вне основного цикла run(), а мне бы хотелось пользоваться сигналами именно внутри цикла. Во-вторых задания могут иметь разный приоритет и мне бы хотелось отрабатывать задания по приоритетам, а не по их поступлению. В-третьих я бы хотел иметь возможность опустошить лист с заданиями или проверить наличие заданий. В общем мне нужно больше функциональности от системы межпоточной коммуникации.

Решение задания я себе представляю таким.
Модули:
1. главный, отвечающий за интерфейс пользователя GUI, с обработкой событий (нажатия на кнопки и прочее) и представление данных на гуе.
2. спидометр (возможные задание: открыть порт, закрыть порт, считывать скорость по мере поступления пакета в порт, закончить поток)
3. компас (аналогично спидометру)
4. GPS (аналогично спидометру)
5. траектория (открыть файл, закрыть файл, считать следующую позицию траектории, закончить поток)
6. алгоритм (получи скорость, получи направление, получи настоящиие координаты, получи заданные координаты траектории, закончить поток)
7. руль (открыть порт, закрыть порт, поверни на определённый угол, закончить поток)
8. педаль газа (аналогично рулю)

В main мы создаём 7 листов с заданиями (массив листов) и стартуем 7 QThread передавая каждому в конструктор этот глобальный массив листов. Каждый модуль может отправлять задания каждому другому модулю. В функции run() каждого модуля имеется цикл while, где в самом начале проверяется есть ли задание для исполнения. Если задания не имеется, то модуль ничего не делает(спит не потребляя никаких ресурсов).


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 19, 2010, 15:54
Честно говоря этот пример показывает, что команды никакие не нужны.
Все сенсоры работают по следующему принципу:
Сенсор::run()
{
   открыть порт;
   while( !exit )
   {
      читать значение;
      сообщить значение;
   }
   закрыть порт;
}

Здесь как раз система сигнал/слот подходит больше всего.
IMHO, не очень удачный пример. Какие задания ты планируешь давать сенсорам?


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 15:58
Самим сенсорам никаких заданий я давать не хочу, а вот программному сенсорному модулю хочу. Задание считать значение например, или считать 10 значений или считывать значения постоянно, пока не придёт другое задание.

Отркыть порт и закрыть порт, тоже являются заданиями, т.к. порт может поменятся во время исполнения программы (если например юзер подключит сенсор к другому COM-порту и поменяет установки программы). Мне тогда что выходить из потока? В принципе тоже возможно, но не желательно.

Я не собираюсь спорить о целесообразности такого способа программирования, я прошу у вас совета как это реализовать в Qt. Как программист я уже профессионально с 2005 года работаю с подобными проектами (на данный момент сделал около 40 проектов). Вот например один из них (см. скрин).
Прога состоит из двух главыных потоков, где один обрабатывает события гуя, а второй выводит данные на гуй, снизу находятся 11 модулей.


Название: Re: Очереди, самодельные сигналы
Отправлено: Авварон от Август 19, 2010, 16:28
[offtop]вы же вроде только недавно знакомы с с++ или я вас с кем-то путаю?
кроме того, программироание на qt отличается от программирования на "обычном" с++\жава и .нете
так что лучше прислушиваться к советам)
[/offtop]
ваш пример реализуется так как вы хотите на обычных плюсах. С сигналами\слотами все будет гораздо проще и как раз способствует разделению на части (ни 1 объект не знает ничего о других, а в вашем случае все они должны знать об очереди)


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 19, 2010, 16:28
Наверное вместо "закончить поток" Вы хотели сказать "усыпить поток", т.е. уйти на ожидание.
Задача выглядит приятно, т.к. в ней много мест независимых друг от друга. Не видно зачем передавать 7 листов в 7 ниток. Первые 4 сами прекрасно знают что им делать. Пятая (траектория) - зависима и запускать для нее нитку не имеет смысла. Лучше просто загрузить весь файл траектории в память и сделать этот расчет частью алгоритма (6). Также с 7 и 8 - если они могут как-то "ответить" (напр. поворот руля завершен) то делать их нитками, иначе незачем.

Механизм взаимодействия ниток - ну практически подойдет любой  :) Не вижу здесь никакой очереди и приоритетов, т.к интересны/существенны только текущие (последние) сигналы с датчиков. Грубо говоря:

- алгоритм (6) делает копию данных посланные датчиками (1-4), выполняет расчет и командует акторами (7-8). Это все

Понимаю что все это выглядит довольно "абстрактно" (недостаточно конкретно) но такова и задача выдуманная Вами  :)


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 17:08
Авварон, не путаете, я правда новичёк в сях и кути. Я выше описал "проблемы" использования системы сигналов и слотов. На вопрос обработки сигнала в run() я ответа не получил. Получается если в слоте/слотах я выполняю действия с обьектом, то зачем мне нужен run()? Я хотел чтобы обьект (как и реальный) жил своей жизнью (параллельно к другим), но если от обьекта ничего не требуется, то и спал бы себе. К советам я прислушиваюсь, поэтому и сижу в форуме, а не программирую сам по себе.

Igors, я имел в виду именно закончить поток (например если юзер захочет выйти ит программы, то все потоки получают задание "exit"), а не усыпить его. Усыпает он сам по себе, когда задание выполнено, и на данный момент нет новых заданий. По примеру, да он не совсем конкретный, т.к. выдуманый мной.
Такой вопрос: если файл с траекторией очень большой (например 1 гиг), то зачем мне держать его в памяти? По остальному может быть где то соглашусь, но т.к. пример абстрактный, то не стоит это обсуждать. Просто иногда бывают не просто сенсоры, а умные сенсоры с настройками или командами. Некоторые сенсоры отсылают данные постоянно, некоторые по запросу. В этом абстрактном примере я просто примерно рассказал о применении такого способа программирования.

Спасибо за Ваши ответы.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 18:27
Если я нажимаю на кнопку на интерфейсе и вызываю слот, который был определён в одном из QThread, то функция в слоте выполняется в том же QThread или в другом, одним на все QThread?
Если объекты находятся в разных потоках (посылающий и принимающий) и используется не DirectConnection, то слот будет отрабатывать в контексте принимающего потока.

Встроил дибаг-сообщения в отправителя сигнала, получателя сигнала и потока. Получатель сигнала (слот) определён в классе QThread, отправитель в главном потоке, DirectConnection не использую. Почемуто номера потоков отправителя и получателя совпадают.

Код:
emit SIGNAL thread id is:  0x15a8
SLOT SerialThread::TX id is:  0x15a8
SerialThread::run() id is:  0x157c


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 19, 2010, 18:36
Встроил дибаг-сообщения в отправителя сигнала, получателя сигнала и потока. Получатель сигнала (слот) определён в классе QThread, отправитель в главном потоке, DirectConnection не использую. Почемуто номера потоков отправителя и получателя совпадают.
Без исходников трудно сказать, но как мне кажется ты не учел, что сам объект QThread создается в контексте создающего потока.
Посмотри на:
void QObject::moveToThread ( QThread * targetThread )

А лучше покажи свой пример, так будет проще все исправить.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 18:43
Да, QThread создаётся в main.cpp(а где ему ещё создаваться?), и отправитель сигнала тоже относится к главному потоку.

Мув посмотрю, спасибо.

Код находится здесь:
http://www.prog.org.ru/index.php?topic=14556.msg95023#msg95023


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 19, 2010, 20:09
На вопрос обработки сигнала в run() я ответа не получил. Получается если в слоте/слотах я выполняю действия с обьектом, то зачем мне нужен run()?
Как уже подробно обсуждалось в этой теме, Вы можете просто построить все на слот/сигнал (обмен через eventLoop), или перекрыть run и сделать свою очередь. Или даже использовать и то и другое (вычурно но возможно).

Я хотел чтобы обьект (как и реальный) жил своей жизнью (параллельно к другим), но если от обьекта ничего не требуется, то и спал бы себе.
...
Такой вопрос: если файл с траекторией очень большой (например 1 гиг), то зачем мне держать его в памяти?
В памяти или читать с диска - не суть. Чтение траектории не выглядит тем что "может жить своей жизнью". Вот если бы Вы такие запросы получали от 2 и более ниток - имело бы смысл делать это параллельно с др. операциями. А если все равно "блок алгоритм" ждет - чего без толку плодить нитки?

Да, QThread создаётся в main.cpp(а где ему ещё создаваться?), и отправитель сигнала тоже относится к главному потоку.
А как connect создавали? Указали тип QueuedConnection?


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 20:14
В памяти или читать с диска - не суть. Чтение траектории не выглядит тем что "может жить своей жизнью". Вот если бы Вы такие запросы получали от 2 и более ниток - имело бы смысл делать это параллельно с др. операциями. А если все равно "блок алгоритм" ждет - чего без толку плодить нитки?
С этим согласен, я был не согласен с тем, чтобы считать всю траекторию в память.

А как connect создавали? Указали тип QueuedConnection?
Пробовал Auto, Queued и Direct, всё один результат.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 20:54
Хотелось бы добить идею со своим листом, а не с сигналами и слотами. Сделал вот так:

Код:
#include "Tasking.h"

Tasking::Tasking()
{
}

Tasking::~Tasking()
{

}

task Tasking::getTask()
{
    while(taskList.empty())
        cond.wait(&mutex, ULONG_MAX);

    mutex.lock();
    task currentTask = taskList.takeFirst();
    mutex.unlock();

    return currentTask;
}

void Tasking::addTask(task newTask)
{
    mutex.lock();
    taskList.append(newTask);
    mutex.unlock();
    cond.wakeOne();
}

Но при запуске проги получаю вот такую ошибку:

Код:
Starte D:\C++\Tasking-build-desktop\debug\Tasking.exe...
ASSERT failure in QMutex::unlock(): "A mutex must be unlocked in the same thread that locked it.", file thread\qmutex.cpp, line 371
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
D:\C++\Tasking-build-desktop\debug\Tasking.exe beendet, Rückgabewert 3

Что не так с мьютексом? С помощью него я хотел защитить обращение (запись/чтение) к QList.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 19, 2010, 21:02
Что не так с мьютексом? С помощью него я хотел защитить обращение (запись/чтение) к QList.
http://www.prog.org.ru/index.php?topic=14426.msg95492#msg95492


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 21:16
Спасибо, теперь не ругается. А можно узнать почему? Пожалуйста.


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 19, 2010, 21:43
Спасибо, теперь не ругается. А можно узнать почему? Пожалуйста.
Доступ к общим данным должен быть защищен.
Смотри что может произойти если ее не защитить:
управление получает первый поток и успевает выполнить проверку, что в очереди есть задания, после этого планировщик переключается на такой же поток, он успевает проверить, что в очереди есть данные и достать оттуда единственное значение. Сейчас очередь уже пуста, происходит переключение обратно на первый поток, он уже выполнил проверку на то, что в очереди есть задания и бросается его доставать, но очередь уже пуста.
Поэтому, даже чтение количества заданий должно быть защищено.

Почему происходила ошибка. Алгоритм работы условных переменных (QWaitCondition) сводится к тому, что вначале мы запираем мьютекс блокирующий данные, проверяем наличие данных, если в нем есть данные, то мы получаем к нему доступ (заметь, запертый мьютекс будет блокировать попытки других потоков добраться до этих данных), а если данных нет - то мы вызываем wait условной переменной связанной с этим мьютексом (указатель на него мы передаем в параметрах wait), wait открывает этот мьютекс (давая возможность другому потоку записать данные) и усыпляет поток.
Другой поток записывает данные и вызывает wakeOne/wakeAll, чем пробуждает поток(и) ждущие в wait. После пробуждения потока, в функции wait блокируется мьютекс и происходит выход из нее.

P.S. Разберись с QMutexLocker, подобные классы значительно облегчают работу с мьютексами и избавляют от ошибок.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 19, 2010, 22:33
Благодарю за разяснение, пока сделал так:

Код:
task Tasking::getTask()
{
    QMutexLocker locker(&mutex);
    while(taskList.empty())
        cond.wait(&mutex, ULONG_MAX);

    return taskList.takeFirst();
}

void Tasking::addTask(task newTask)
{
    QMutexLocker locker(&mutex);
    taskList.append(newTask);
    cond.wakeOne();
}

ЗЫ с ответами какашко получилось, сори, я стёр свой предыдущий ответ.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 23, 2010, 16:06
Привет!

Появилась новая проблема. Сначала я запускал в main два потока и потом QApplication::exec(). Но ивенты не обрабатывались и окно не прорисовывалось, т.к. QApplication::exec() был блокирован двумя потоками. Немного поразмыслив, я решил запустить третью нить, в функции run() которой, я собирался выполнить QApplication::exec(), то есть запустить главный поток параллельно двум другим.
При старте прога зависает и пишет QApplication::exec: Must be called from the main thread.

А как же мне ещё сделать обработку параллельно?

Выкладываю main.cpp

Код:
#include <QList>

#include "mainwidget.h"
#include "Tasking.h"
#include "guithread.h"
#include "thread1.h"
#include "thread2.h"

int main(int argc, char *argv[])
{
    QList<Tasking*> allTasks;

    Tasking *tasking1 = new Tasking;
    Tasking *tasking2 = new Tasking;
    allTasks.append(tasking1);
    allTasks.append(tasking2);

    GUIThread guiThread(allTasks, argc, argv);
    Thread1 myThread1(allTasks);
    Thread2 myThread2(allTasks);

    QObject::connect(&myThread1, SIGNAL(showData(QString)), guiThread.w, SLOT(addText(QString)));
    QObject::connect(&myThread2, SIGNAL(showData(QString)), guiThread.w, SLOT(addText(QString)));

    guiThread.start();
    myThread1.start();
    myThread2.start();
    guiThread.wait();
    myThread1.wait();
    myThread2.wait();

    return 0;
}

guithread.cpp

Код:
#include "guithread.h"

GUIThread::GUIThread(const QList<Tasking*>& allTasks, int argc, char *argv[])
{
    Tasks = allTasks;
    qapp = new QApplication(argc, argv);
    w = new MainWidget(Tasks);
    w->showMW();
}

void GUIThread::run()
{
    qapp->exec();
}


Ну и на всякий случай код потока (второй сделан аналогично)

Код:
#include "thread1.h"

Thread1::Thread1(const QList<Tasking*>& allTasks)
{
    Tasks = allTasks;
}

void Thread1::run()
{
    task myTask;
    bool exit = FALSE;
    while(!exit)
    {
        myTask = Tasks[0]->getTask();
        switch (myTask.tasknum)
        {
            case 0:
                exit = TRUE;
            case 1:
                emit showData(myTask.data);
                Tasks[1]->addTask(myTask);
        }
    }
}

Спасибо!


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 23, 2010, 16:32
QApplication::exec: Must be called from the main thread
это обязательное условие. Нельзя работать с GUI в других потоках, только главный.
Вызывай exec() в функции main().

А вот ожидать завершения дочерних потоков после старта не нужно (убери вызовы wait()).


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 23, 2010, 16:38
Спасибо, Вы так имели в виду?

Код:
    myThread1.start();
    myThread2.start();
    qapp.exec();
    myThread1.wait();
    myThread2.wait();


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 23, 2010, 16:44
Сначала я запускал в main два потока и потом QApplication::exec(). Но ивенты не обрабатывались и окно не прорисовывалось, т.к. QApplication::exec() был блокирован двумя потоками.
Не должен он быть блокирован, на то они и нитки чтобы выполняться параллельно

Код:
    myThread1.start();
    myThread2.start();
    qapp.exec();
    myThread1.wait();
    myThread2.wait();
Да, это правильно и используется в большинстве случаев


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 23, 2010, 16:51
Спасибо, Вы так имели в виду?
Скорее всего ты будешь останавливать свои потоки в какой то другой функции (когда они будут не нужны), вот там вызов wait() будет более уместен.


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 23, 2010, 16:55
Ура!!! Всё получилось, работает как я и ожидаю!
Огромнейшее спасибо Вам!
Выкладываю рабочий проект на Ваш просмотр.


Название: Re: Очереди, самодельные сигналы
Отправлено: Igors от Август 23, 2010, 17:15
Скорее всего ты будешь останавливать свои потоки в какой то другой функции (когда они будут не нужны), вот там вызов wait() будет более уместен.
Здесь myThread1.wait() не остановка, а ожидание завершения нитки (pthread_join). Или я не понял и и речь идет о др. wait?  :)


Название: Re: Очереди, самодельные сигналы
Отправлено: labview от Август 23, 2010, 17:25
По нажатию на кнопку Exit, оба (все) потока получают задание с номером 0, что означает закончить цикл потока.


Название: Re: Очереди, самодельные сигналы
Отправлено: BRE от Август 23, 2010, 17:45
Здесь myThread1.wait() не остановка, а ожидание завершения нитки (pthread_join). Или я не понял и и речь идет о др. wait?  :)
Я про то, что wait() уместней ставить после команды остановить поток.