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

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

Страниц: 1 [2]   Вниз
  Печать  
Автор Тема: GUI-thread и Child-threads  (Прочитано 13974 раз)
HunteX
Гость
« Ответ #15 : Май 18, 2011, 13:05 »

А значит, необходимо передавать в класс ThreadClass семафор и мьютекс ... скажем в конструкторе ThreadClass() ?
Просто сделать их static членами ThreadClass, т.к. они должны использоваться всеми нитками которые грузят имеджи

Что за ошибка не пойму? Вроде сделал:
Код:
#include <QSemaphore>
#include <QList>
#include <QMutex>

p.s. Причем, если убираю static, то все отлично компилируется



код такой:
Код:
public:
    explicit MegaTransformer(QWidget *parent = 0);
~MegaTransformer();

static QSemaphore semaphore;
static QMutex mutex;
static QList<TaskClass> taskList;
« Последнее редактирование: Май 18, 2011, 13:53 от HunteX » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #16 : Май 18, 2011, 13:49 »

Забыли объявить сами static члены - помимо их описания в классе они должны быть в одном из cpp файлов

QSemaphore megaTransgormer::semaphore;
и.т.п
Записан
HunteX
Гость
« Ответ #17 : Май 18, 2011, 13:57 »

Забыли объявить сами static члены - помимо их описания в классе они должны быть в одном из cpp файлов

QSemaphore megaTransgormer::semaphore;
и.т.п
Ура! Спасибо, а то я совсем забыл про это Грустный Тяжело с C# на C++ переходить
Записан
HunteX
Гость
« Ответ #18 : Май 18, 2011, 15:07 »

Теперь еще одна проблема (((
У меня получилось 3 класса:
1. MegaTransformer (megatransformer.h)
2. ThreadClass (HunteXThreadClass.h)
3. TaskClass (HunteXTaskClass.h)

Хидер MegaTransformer.h:
Код:
#define QT_THREAD_SUPPORT
...
...
#include "ui_megatransformer.h"
#include "HunteXTaskClass.h" // ссылаемся на TaskClass

namespace Ui
{
    class MegaTransformer;
}

class MegaTransformer : public QMainWindow
{
Q_OBJECT

public:
    explicit MegaTransformer(QWidget *parent = 0);
~MegaTransformer();

static QSemaphore semaphore;
static QMutex mutex;
static QList<TaskClass> taskList;

private slots:
void loadImages(); // слот, который открывает QFileDialog
void timerUpdate(); // UI
void loadingAllDone(); // пока не юзается

private:
    Ui::MegaTransformer *ui;

QTimer *timer;

void InitObjects();
void AddImageToProcess(char* imagePath);
};

Мне надо добавить объект класса ThreadClass в класс MegaTransformer, но для этого нужно добавить инклуд на файл ThreadClass (HunteXThreadClass.h)

Если я добавляю в инклуд хидер класса ThreadClass, то получаю следующую ошибку:
error C2011: ThreadClass: переопределение типа "class" ... HunteXThreadClass.h MegaTransformer

Возможно проблема в том, что у меня в файле
Код:
#include "HunteXThreadClass.h"
#include "megatransformer.h" // возможно поэтому такая ошибка, но без этого хидера никак, так как юзаются MegaTransformer::semaphore|mutex

void ThreadClass::run()
{
while (true)
{
MegaTransformer::semaphore.acquire(); // ждем заданий от главной нитки
MegaTransformer::mutex.lock();            // извлекаем задание


Тогда пробую добавить объект класса ThreadClass в файл megatransformer.cpp (главный поток).
Код:
...
#include "megatransformer.h"
#include "HunteXThreadClass.h"

QSemaphore MegaTransformer::semaphore;
QMutex MegaTransformer::mutex;
QList<TaskClass> MegaTransformer::taskList;

ThreadClass *threads;

void MegaTransformer::InitObjects()
{
     threads = new ThreadClass();
}

void MegaTransformer::loadImages()
{
     threads->run(); // а вот тут получаю ошибку (см. ниже)
}

error C2248: ThreadClass::run: невозможно обратиться к protected член, объявленному в классе "ThreadClass" MegaTransformer
« Последнее редактирование: Май 18, 2011, 15:09 от HunteX » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #19 : Май 18, 2011, 15:32 »

error C2248: ThreadClass::run: невозможно обратиться к protected член, объявленному в классе "ThreadClass" MegaTransformer
Правильно, для запуска надо вызывать start() который сам позовет run() (а не звать run() напрямую). Assistant-то открывайте, с такими вопросами беспокоить форум не гуд  Улыбающийся
Записан
HunteX
Гость
« Ответ #20 : Май 21, 2011, 13:52 »

Всё отлично! Получилось сделать программу ... реализация работает в 2-х потоках: GUI и в потоке, в котором ведется работа с изображением:

1. Загрузка (read-only)
2. Операция А (read-only)
3. Операция B (read-only)
4. Операция трансформации (write)
5. Запись результатов

Пока все делается последовательно ... Хотелось бы таким образом:
После загрузки изображения (п. 1) нужно вынести операции из п. 2, 3 (А, В) в отдельные потоки, причем запустить одновременно и дожидаться окончания этих операций. После чего выполнить последовательно операции 4, 5.

Я предполагаю сделать следующее:

Код
C++ (Qt)
void ThreadClass::run()
{
while (true)
{
MegaTransformer::semaphore.acquire(); // ждем заданий от главной нитки
MegaTransformer::mutex.lock();            // извлекаем задание
 
TaskClass task = MegaTransformer::taskList.takeAt(0);
MegaTransformer::mutex.unlock();
 
if (task.imagePath == NULL)  break;  // пустое имя значит завершить нитку
 
task.OpenImage(); // загрузка изображения
emit signalLoad(task.numberOfImage, task.imagePath); // сообщаем что загрузили
 
// ==========================================================================================
// ВИДИМО ВОТ ЭТУ ЧАСТЬ ЛУЧШЕ ВСЕГО ВЫДЕЛИТЬ В ОТДЕЛЬНЫЙ ПОТОК ? ТОЧНЕЕ ПО ПОТОКУ НА ОПЕРАЦИИ А, В
task.OperationA();  // Операция А
task.OperationВ();  // Операция В
// ==========================================================================================
 
                 emit AllOperationsFinished(...); // ждем выполнения операций А, В
 
... выполнение операции 4 + ожидание выполнения
 
... выполнение операции 5 + ожидание выполнения
}
}

Получается, что надо создавать подобный класс, наследовать от QThread и переопределять метод run(), который будет выполнять операции А, В, а также создавать объекты Mutex, Semaphore ? Причем надо дождаться выполнениях обеих операций А и В ... а уже потом начинать п. 4
« Последнее редактирование: Май 21, 2011, 14:23 от HunteX » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Май 21, 2011, 15:52 »

Получается, что надо создавать подобный класс, наследовать от QThread и переопределять метод run(), который будет выполнять операции А, В, а также создавать объекты Mutex, Semaphore ? Причем надо дождаться выполнениях обеих операций А и В ... а уже потом начинать п. 4
Достоинство "очереди" в том что она универсальна. Вместо того чтобы делать новый класс от QThread  (который по существу будет отличаться только обработчиком) проще расширить TaskClass. напр
Код
C++ (Qt)
bool TaskClass::Process( void )
{
 switch (mCommand) {
  case cmd_OpenImage:
    OpenImage():    // выполняем команду
    ...
    AddImage2Process(CTask(cmd_ScaleImage, ...);  // помещаем новую команду в очередь
    break;
 
  case cmd_ScaleImage:
   ...
   break;
 
  case cmd_EndOfJob:   // специальная команда - выйти
    return false;
 }
 return true;
}
 
« Последнее редактирование: Май 21, 2011, 15:54 от Igors » Записан
HunteX
Гость
« Ответ #22 : Май 21, 2011, 16:42 »

Достоинство "очереди" в том что она универсальна. Вместо того чтобы делать новый класс от QThread  (который по существу будет отличаться только обработчиком) проще расширить TaskClass. напр

Да, действительно, я не подумал, ведь это намного удобнее ...

Вопросик по поводу кода:
Код:
case cmd_OpenImage:
     OpenImage():    // выполняем команду
     ...
     AddImage2Process(CTask(cmd_ScaleImage, ...);  // помещаем новую команду в очередь
     break;

Так как у меня функция AddImage2Process() находится в другом классе, наверное стоит просто добавить задание в тасклист вручную при помощи push_back()?

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

У меня возник вопрос по коду, который Вы мне посоветовали использовать ранее:

Код
C++ (Qt)
QList<TaskClass> MegaTransformer::taskList;
 
void MegaTransformer::loadImages()
{
QStringList imagesPath = QFileDialog::getOpenFileNames(...)
 
for(int i=0; i<imagesPath.length();i++)
{
AddImageToProcess(imagesPath[i]);
}
 
threads->start();
}
 
void MegaTransformer::AddImageToProcess(QString imagePath)
{
QMutexLocker locker(&mutex);
 
TaskClass task; // создание объекта TaskClass
          // ... и последующее заполнение ...
 
taskList.push_back(task); // добавление задачи в очередь
semaphore.release();   // есть работа для ниток-загрузчиков
}
 
void ThreadClass::run()
{
while (true)
{
MegaTransformer::semaphore.acquire(); // ждем заданий от главной нитки
MegaTransformer::mutex.lock();        // извлекаем задание
 
TaskClass task = MegaTransformer::taskList.takeAt(0);
MegaTransformer::mutex.unlock();
 
task.OpenImage(); // загрузка изображения
emit signalLoad(task.numberOfImage, task.imagePath); // сообщаем что загрузили
// .....
}
}

1. Там ли я запускаю фунцию threads->start(); Может стоит вызывать её ранее, скажем в конструкторе MegaTransformer ?
2. Обязательно ли блокировка мьютекса ( QMutexLocker locker(&mutex); ) в функции AddImageToProcess() ? Собственно, именно это меня интересует больше всего.
« Последнее редактирование: Май 21, 2011, 16:52 от HunteX » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #23 : Май 21, 2011, 17:13 »

Так как у меня функция AddImage2Process() находится в другом классе, наверное стоит просто добавить задание в тасклист вручную при помощи push_back()?
push_back рухнет без мутекса если др. нитка в этот момент делает takeAt (или тот же push_back) - ведь контейнеры НЕ "потокобезопасны". Не стоит делать ф-цию членом класса только потому что она в нем вызывается - важнее данные какого класса ф-ция использует. Неплохо напр сделать AddImage2Process static ф-цией TaskClass

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

1. Там ли я запускаю фунцию threads->start(); Может стоит вызывать её ранее, скажем в конструкторе MegaTransformer ?
Ну работать будет и так и так, но если бороться "за чистоту рядов" - то чего это MegaTransformer сам решает толкать нитки или нет? На мой взгляд это дело того кто им управляет (главной нитки)

2. Обязательно ли блокировка мьютекса ( QMutexLocker locker(&mutex); ) в функции AddImageToProcess() ? Собственно, именно это меня интересует больше всего.
Совершенно обязательно (см. ответ на первый вопрос)
Записан
HunteX
Гость
« Ответ #24 : Май 22, 2011, 18:10 »

Вот еще странная вещь ...
инициирую сигнал при помощи emit, передаю в слот double[] | std::list | QList, любое на выбор ...
Но слот сигнал не ловит ... меняю тип к примеру на int - все работает ...

Код
C++ (Qt)
class ThreadClass : public QThread
{
  ...
  signals:
    void signalHomo(std::list<double>);
}
 
ThreadClass::run()
{
 ....
 std::list<double> matrix;
 ...
 emit signalHomo(matrix);
}
 
class MegaTransformer : public QMainWindow
{
   ...
   private slots:
     void homoDone(std::list<double>);
}
 
connect(threads, SIGNAL(signalHomo(std::list<double>)), this, SLOT(homoDone(std::list<double>)), Qt::QueuedConnection);
 
void MegaTransformer::homoDone(std::list<double> matrix)
{
  // сигнал не поступает :(
}
« Последнее редактирование: Май 22, 2011, 19:30 от HunteX » Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #25 : Май 22, 2011, 19:50 »

Но слот сигнал не ловит ... меняю тип к примеру на int - все работает ...
А в консоль приложения мы принципиально не смотрим?
Цитировать
QObject::connect: Cannot queue arguments of type 'QList<double>'
(Make sure 'QList<double>' is registered using qRegisterMetaType().)
Где угодно до первого такого connect'а:
Код
C++ (Qt)
qRegisterMetaType< QList<double> >( "QList<double>" );
Записан
HunteX
Гость
« Ответ #26 : Май 22, 2011, 20:33 »

Спасибо! Я на студии пишу, там 100500+ строк кода в вывод вываливает, но впредь буду туда поглядывать
Записан
Страниц: 1 [2]   Вверх
  Печать  
 
Перейти в:  


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