Название: GUI-thread и Child-threads Отправлено: HunteX от Май 17, 2011, 12:43 Здравствуйте! Есть форма с парой кнопок. При нажатии на кнопку должен создаваться поток, в котором будет происходить загрузка большого изображения (впринципе, тип работы не особо важен, главное, что работать будет долго, именно поэтому не хотелось бы, чтобы основной поток зависал). Я новичек в Qt, на C# я без проблем напишу, а с кьюти пока не совсем разобрался ... Просьба такая: накидайте небольшой (как можно проще) шаблончик, который будет выполнять действия, описанные мною выше. Спасибо!
С Уважением, Андрей. p.s. конечно же предполагается загрузка не одного изображения, а нескольких ... вообщем нужен шаблон использования некого аналога ThreadPool в .NET Название: Re: GUI-thread и Child-threads Отправлено: Пантер от Май 17, 2011, 13:02 Читай (http://doc.qt.nokia.com/4.7/threads.html)
Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 17, 2011, 14:34 Читай (http://doc.qt.nokia.com/4.7/threads.html) Не сомневался :) Название: Re: GUI-thread и Child-threads Отправлено: Авварон от Май 17, 2011, 15:09 Фигасе, 30 долларов за книжку по каким-то тредам...
Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 17, 2011, 15:53 Просьба такая: накидайте небольшой (как можно проще) шаблончик, который будет выполнять действия, описанные мною выше. Спасибо! Весьма меркантильное желание "использовать" - но поскольку Вы абсолютно вежливы/корректны - претензий быть не может :)По существу - "кидать" особо нечего. Запихиваете свою содержательную часть в run QThread (наследуетесь), толкаете его с помощью метода start, принимаете результаты по сигналу finished - ну или (если есть необходимость) ждете методом wait. То есть у Вас легкое/приятное "многопоточное программирование" Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 17, 2011, 16:28 Просьба такая: накидайте небольшой (как можно проще) шаблончик, который будет выполнять действия, описанные мною выше. Спасибо! Весьма меркантильное желание "использовать" - но поскольку Вы абсолютно вежливы/корректны - претензий быть не может :)По существу - "кидать" особо нечего. Запихиваете свою содержательную часть в run QThread (наследуетесь), толкаете его с помощью метода start, принимаете результаты по сигналу finished - ну или (если есть необходимость) ждете методом wait. То есть у Вас легкое/приятное "многопоточное программирование" Спасибо за совет! У меня пара вопросов: Цитировать Запихиваете свою содержательную часть в run QThread Код: void Thread::run(Object *myobject); // так можно ? Цитировать принимаете результаты по сигналу finished А вот тут можно по подробнее?Если я передаю Thread::run(...) некий НЕ глобальный параметр, как я могу получить результат? Если объект глобальный - тут все понятно: можно послать событие о завершении Thread::run() и потом обратися к этому глобальному объекту - результату работы Thread::run(). А как быть с локальным объектом ? Название: Re: GUI-thread и Child-threads Отправлено: ieroglif от Май 17, 2011, 16:31 создай свой класс, наследующий QThread, переопредели в нём run() метод, в котором опиши действия выполняемые потоком (загрузка картинок)
создай объект своего класса, вызови ему start() Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 17, 2011, 16:48 Код: void Thread::run(Object *myobject); // так можно ? А вот тут можно по подробнее? Выберите стратегию/метод что Вам нравится. Типичные случаиЕсли я передаю Thread::run(...) некий НЕ глобальный параметр, как я могу получить результат? Если объект глобальный - тут все понятно: можно послать событие о завершении Thread::run() и потом обратися к этому глобальному объекту - результату работы Thread::run(). А как быть с локальным объектом ? - нитка запускается для решения одной/конкретной задачи. Перекрываете run и заряжаете параметры напр в конструкторе нитки - нитка принимает события (может быть использована для решения одной или многих (разных) задач) - подаете параметры в сигнале Как получить результат. Самое простое - по сигналу который испустит сама нитка когда сделает все что нужно. Если от нее ничего больше не требуется - задействовать стандартный сигнал finished Название: Re: GUI-thread и Child-threads Отправлено: Пантер от Май 17, 2011, 16:51 Есть еще QtConcurrent, можешь его заюзать.
Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 17, 2011, 18:55 Есть еще QtConcurrent, можешь его заюзать. Я с него начал, даже сделал то, что надо наполовину, но показалось слишком сложно и я удалил код :)Вот, набросал класс (не ругайте сильно, так как я новичек не только в Qt, но и в C++, да и про ООП все позабывал, сейчас вспоминаю): header Код: #include <QThread> source Код: #include "megatransformer.h" К примеру, у меня есть функция (пускай будет foo()), в которой я получаю пути всех картинок (при помощи QFileDialog::getOpenFileNames()), как мне юзать мой класс? Догадываюсь, что для каждой картинки придется создавать объект ThreadClass и засовывать его в пул потоков ... я правильно понял? И еще помогите разобраться с сигналами и слотами ... Вопросы такие: 1. Где именно надо вызывать connect? (думаю, что в функции foo(), после создания объекта ThreadClass?) 2. Как получить событие, что функция ThreadClass::OpenImage() закончила свою работу? (наверное я зря их сделал private?) Спасибо! Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 18, 2011, 06:52 1. Где именно надо вызывать connect? (думаю, что в функции foo(), после создания объекта ThreadClass?) Да2. Как получить событие, что функция ThreadClass::OpenImage() закончила свою работу? (наверное я зря их сделал private?) Просто посылаете сигнал из run в главную нитку (connect с типом QueuedConnection)Догадываюсь, что для каждой картинки придется создавать объект ThreadClass и засовывать его в пул потоков ... я правильно понял? Я бы не связывался с пулом а сделал бы простую очередь. Создаете структуру/класс с теми полями что Вы сделали для ThreadClass, допустим CTask. Толкаете N ниток (idealThreadCount), run выглядит примерно такКод А главная нитка помещает в очередь Код И еще scene->clear(); Убедитесь что эти вызовы "потокобезопасны" иначе перенесите их в главную ниткуscene->addPixmap(sized); Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 18, 2011, 09:25 То есть в начале из GUI-потока вызывается функция ThreadClass::AddImage2Process(), после чего выполняется ThreadClass::run() ?
Код: CMutexLocker locker(&theMutex); p.s. Спасибо за код! Я только начинал разбираться и хотелось бы с начала написать правильный велосипед :) Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 18, 2011, 10:10 То есть в начале из GUI-потока вызывается функция ThreadClass::AddImage2Process(), после чего выполняется ThreadClass::run() ? Необов'язково. Если у run нет работы - он будет ждать на семафоре. AddImage2Process делает главная нитка (а не ThreadClass)А вот насчет этого я не совсем понял: нужно отдельный класс создавать для хранения мьютекса ? То есть не совсем понятно, зачем это нужно и что выполняет этот класс и как он выглядит ... возможно имелся ввиду QMutexLocker ? Да, "очепятка", подправил :)Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 18, 2011, 11:01 То есть в начале из GUI-потока вызывается функция ThreadClass::AddImage2Process(), после чего выполняется ThreadClass::run() ? Необов'язково. Если у run нет работы - он будет ждать на семафоре. AddImage2Process делает главная нитка (а не ThreadClass)А вот насчет этого я не совсем понял: нужно отдельный класс создавать для хранения мьютекса ? То есть не совсем понятно, зачем это нужно и что выполняет этот класс и как он выглядит ... возможно имелся ввиду QMutexLocker ? Да, "очепятка", подправил :)А значит, необходимо передавать в класс ThreadClass семафор и мьютекс ... скажем в конструкторе ThreadClass() ? Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 18, 2011, 11:18 А значит, необходимо передавать в класс ThreadClass семафор и мьютекс ... скажем в конструкторе ThreadClass() ? Просто сделать их static членами ThreadClass, т.к. они должны использоваться всеми нитками которые грузят имеджиНазвание: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 18, 2011, 13:05 А значит, необходимо передавать в класс ThreadClass семафор и мьютекс ... скажем в конструкторе ThreadClass() ? Просто сделать их static членами ThreadClass, т.к. они должны использоваться всеми нитками которые грузят имеджиЧто за ошибка не пойму? Вроде сделал: Код: #include <QSemaphore> p.s. Причем, если убираю static, то все отлично компилируется (http://img856.imageshack.us/img856/6393/errorsz.png) код такой: Код: public: Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 18, 2011, 13:49 Забыли объявить сами static члены - помимо их описания в классе они должны быть в одном из cpp файлов
QSemaphore megaTransgormer::semaphore; и.т.п Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 18, 2011, 13:57 Забыли объявить сами static члены - помимо их описания в классе они должны быть в одном из cpp файлов Ура! Спасибо, а то я совсем забыл про это :( Тяжело с C# на C++ переходитьQSemaphore megaTransgormer::semaphore; и.т.п Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 18, 2011, 15:07 Теперь еще одна проблема (((
У меня получилось 3 класса: 1. MegaTransformer (megatransformer.h) 2. ThreadClass (HunteXThreadClass.h) 3. TaskClass (HunteXTaskClass.h) Хидер MegaTransformer.h: Код: #define QT_THREAD_SUPPORT Мне надо добавить объект класса ThreadClass в класс MegaTransformer, но для этого нужно добавить инклуд на файл ThreadClass (HunteXThreadClass.h) Если я добавляю в инклуд хидер класса ThreadClass, то получаю следующую ошибку: error C2011: ThreadClass: переопределение типа "class" ... HunteXThreadClass.h MegaTransformer Возможно проблема в том, что у меня в файле Код: #include "HunteXThreadClass.h" Тогда пробую добавить объект класса ThreadClass в файл megatransformer.cpp (главный поток). Код: ... error C2248: ThreadClass::run: невозможно обратиться к protected член, объявленному в классе "ThreadClass" MegaTransformer Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 18, 2011, 15:32 error C2248: ThreadClass::run: невозможно обратиться к protected член, объявленному в классе "ThreadClass" MegaTransformer Правильно, для запуска надо вызывать start() который сам позовет run() (а не звать run() напрямую). Assistant-то открывайте, с такими вопросами беспокоить форум не гуд :)Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 21, 2011, 13:52 Всё отлично! Получилось сделать программу ... реализация работает в 2-х потоках: GUI и в потоке, в котором ведется работа с изображением:
1. Загрузка (read-only) 2. Операция А (read-only) 3. Операция B (read-only) 4. Операция трансформации (write) 5. Запись результатов Пока все делается последовательно ... Хотелось бы таким образом: После загрузки изображения (п. 1) нужно вынести операции из п. 2, 3 (А, В) в отдельные потоки, причем запустить одновременно и дожидаться окончания этих операций. После чего выполнить последовательно операции 4, 5. Я предполагаю сделать следующее: Код
Получается, что надо создавать подобный класс, наследовать от QThread и переопределять метод run(), который будет выполнять операции А, В, а также создавать объекты Mutex, Semaphore ? Причем надо дождаться выполнениях обеих операций А и В ... а уже потом начинать п. 4 Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 21, 2011, 15:52 Получается, что надо создавать подобный класс, наследовать от QThread и переопределять метод run(), который будет выполнять операции А, В, а также создавать объекты Mutex, Semaphore ? Причем надо дождаться выполнениях обеих операций А и В ... а уже потом начинать п. 4 Достоинство "очереди" в том что она универсальна. Вместо того чтобы делать новый класс от QThread (который по существу будет отличаться только обработчиком) проще расширить TaskClass. напрКод
Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 21, 2011, 16:42 Достоинство "очереди" в том что она универсальна. Вместо того чтобы делать новый класс от QThread (который по существу будет отличаться только обработчиком) проще расширить TaskClass. напр Да, действительно, я не подумал, ведь это намного удобнее ... Вопросик по поводу кода: Код: case cmd_OpenImage: Так как у меня функция AddImage2Process() находится в другом классе, наверное стоит просто добавить задание в тасклист вручную при помощи push_back()? В данный момент мое приложение осуществляет последовательную работу (загрузка, обработка, сохранение) с изображением, правильным ли будет выбрать очередь? Сейчас все операции производятся только с одним изображением, потом с другим ... А что если мне надо реализовать последовательную загрузку изображений, но их параллельную обработку? То есть загрузили изображение - запускаем его обработку и одновременно открываем следующее изображение ... (распараллеливать загрузку изображений в моем случае не имеет смысла, так как винчестер один, получится только хуже) У меня возник вопрос по коду, который Вы мне посоветовали использовать ранее: Код
1. Там ли я запускаю фунцию threads->start(); Может стоит вызывать её ранее, скажем в конструкторе MegaTransformer ? 2. Обязательно ли блокировка мьютекса ( QMutexLocker locker(&mutex); ) в функции AddImageToProcess() ? Собственно, именно это меня интересует больше всего. Название: Re: GUI-thread и Child-threads Отправлено: Igors от Май 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() ? Собственно, именно это меня интересует больше всего. Совершенно обязательно (см. ответ на первый вопрос)Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 22, 2011, 18:10 Вот еще странная вещь ...
инициирую сигнал при помощи emit, передаю в слот double[] | std::list | QList, любое на выбор ... Но слот сигнал не ловит ... меняю тип к примеру на int - все работает ... Код
Название: Re: GUI-thread и Child-threads Отправлено: LisandreL от Май 22, 2011, 19:50 Но слот сигнал не ловит ... меняю тип к примеру на int - все работает ... А в консоль приложения мы принципиально не смотрим?Цитировать QObject::connect: Cannot queue arguments of type 'QList<double>' Где угодно до первого такого connect'а:(Make sure 'QList<double>' is registered using qRegisterMetaType().) Код
Название: Re: GUI-thread и Child-threads Отправлено: HunteX от Май 22, 2011, 20:33 Спасибо! Я на студии пишу, там 100500+ строк кода в вывод вываливает, но впредь буду туда поглядывать
|