Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Qt: образец для правильной работы с потоками — больше добротная реализация

Anna | 24.06.2014 | нет комментариев
В своей предыдущей статье я затронул тему грамотной реализации потоков в Qt и предложил свой вариант. Вкомментариях мне подсказали больше правильное направление. Испробовал сделать — получилось и воистину легко и прекрасно! Я хотел было поправить ветхую статью, но Прогр повис — и все потерялось. В выводе я решил написать новую версию.

Сейчас мы за основу мы возьмем QThread и сделаем от него шаблонного преемника (Шлее реабилитирован!). Подход будет дальнейший:

  1. создание потока QThread;
  2. в нем в нынешнем потоке подготавливается информация для нового потока;
  3. заказчик вызывает starting (priority)…
  4. … и в переопределенном способе run () — в новом потоке — создается необходимый объект, устанавливаются связи, вызывается сигнал «объект готов» и запускается цикл обработки сообщений;
  5. заказчик в начальном потоке получает сигнал и новейший объект.

Как и в прошлый раз, помним о неосуществимости MOCом обработать шаблонный класс: «MOC не разрешает применять все вероятности С . Основная задача в том, что образцы классов не могут иметь сигналы либо слоты».

Реализация

Разглядим код сделанных классов (Дабы оно все влезло в экран, я убрал комментарии):

// **
// **  Базовый класс для потока
// **

class ThreadedObjectBase: public QThread
{
	Q_OBJECT

protected:
	const char *_finished_signal;
	const char *_terminate_slot;
	bool _to_delete_later_object;

	void initObject (QObject *obj)
	{
		bool res;
		if (_finished_signal)
		{
			res = connect (obj, _finished_signal, this, SLOT (quit ()));
			Q_ASSERT_X (res, "connect", "connection is not established");
		}
		if (_terminate_slot)
		{
			res = connect (this, SIGNAL (finished ()), obj, _terminate_slot);
			Q_ASSERT_X (res, "connect", "connection is not established");
		}
		if (_to_delete_later_object && _finished_signal)
		{
			res = connect (obj, _finished_signal, obj, SLOT (deleteLater ()));	
			Q_ASSERT_X (res, "connect", "connection is not established");
		}
		emit objectIsReady ();
	}

public:
	ThreadedObjectBase (QObject *parent = 0): QThread (parent), 
		_finished_signal (0), _terminate_slot (0), _to_delete_later_object (true)	{}

signals:
	void objectIsReady (void);
};

// **
// **  Шаблонный класс для потока
// **

template <class T>
class ThreadedObject: public ThreadedObjectBase
{
protected:
	T	*_obj;

public:
	ThreadedObject (QObject *parent = 0): ThreadedObjectBase (parent), _obj (0) 	{}

	void starting (
		const char *FinishedSignal = 0,
		const char *TerminateSlot = 0,
		QThread::Priority Priority = QThread::InheritPriority,
		bool ToDeleteLaterThread = true,
		bool ToDeleteLaterObject = true)
	{
		_finished_signal = FinishedSignal;
		_terminate_slot = TerminateSlot;
		_to_delete_later_object = ToDeleteLaterObject;
		start (Priority);
	}

	void run (void)			{ initObject (_obj = new T); exec (); }

	bool objectIsCreated (void) const	{ return _obj != 0; }

	T* ptr (void) 			{ return reinterpret_cast <T*> (_obj); }
	const T* cptr (void) const		{ return reinterpret_cast <const T*> (_obj); }

	operator T* (void)			{ return ptr (); }
	T* operator -> (void)		{ return ptr (); }
	operator const T* (void) const	{ return cptr (); }
	const T* operator -> (void) const	{ return cptr (); }
};

Здесь стержневой способ — starting, тот, что запоминает имена сигналов и слотов, а также устанавливает отложенное удаление способа. Способ objectIsCreated () возвращает истину когда объект теснее сделан. Бесчисленные перегрузки разрешают применять ThreadedObject<T> как «разумный» указатель.

Вот простенький пример применения этих классов:

ThreadedObject <Operation> _obj;
QObject::connect (&_obj, SIGNAL (objectIsReady ()), this, SLOT (connectObject ()));
_obj.starting (SIGNAL (finished ()), SLOT (terminate ()), QThread::HighPriority);

Снизу прилагается настоящий пример — в основном потоке создается кнопка. В новом потоке создается переменная типа int, а также сигнал от таймера и событие по таймеру. Оба этих таймера сокращают значение переменной int, по достижению нуля вызывается слот QCoreApplication::quit (). С иной стороны, закрытие приложения останавливает поток. Пример проверен в WinXP. Хотелось бы в комментариях услышать об удачных испытаниях в Linux,MacOS, Android и прочих поддерживаемых платформах.

Пример классы

Файл ThreadedObject:

#include <QtCore>

// **
// **  Базовый класс для потока
// **

class ThreadedObjectBase: public QThread
{
	Q_OBJECT

protected:
	const char *_finished_signal;		// имя сигнала "окончание работы объекта"
	const char *_terminate_slot;		// имя слота "остановка работы"
	bool _to_delete_later_object;		// установка отложенного удаление объекта

	// . настройка
	void initObject (QObject *obj)
	{
		bool res;
		if (_finished_signal)		// установить сигнал "окончание работы объекта"?
		{	res = connect (obj, _finished_signal, this, SLOT (quit ()));			Q_ASSERT_X (res, "connect", "connection is not established");	}	// по окончанию работы объекта поток будет закончен
		if (_terminate_slot)		// установить слот "остановка работы"?
		{	res = connect (this, SIGNAL (finished ()), obj, _terminate_slot);	Q_ASSERT_X (res, "connect", "connection is not established");	}	// перед остановкой потока будет вызван слот объекта "остановка работы"
		if (_to_delete_later_object && _finished_signal)	// установить отложенное удаление объекта?
		{	res = connect (obj, _finished_signal, obj, SLOT (deleteLater ()));	Q_ASSERT_X (res, "connect", "connection is not established");	}	// по окончанию работы объекта будет установлено отложенное удаление
		emit objectIsReady ();		// объект готов к работе
	}

public:
	ThreadedObjectBase (QObject *parent = 0): QThread (parent){}

signals:
	void objectIsReady (void);			// сигнал "объект запущен"
};	// class ThreadedObject

// **
// **  Шаблонный класс для потока
// **

template <class T>
class ThreadedObject: public ThreadedObjectBase
{
protected:
	T	*_obj;		// объект, исполняемый в новом потоке

public:
	ThreadedObject (QObject *parent = 0): ThreadedObjectBase (parent), _obj (0)	{}

	// . настройка
	void starting (const char *FinishedSignal = 0, const char *TerminateSlot = 0, QThread::Priority Priority = QThread::InheritPriority, bool ToDeleteLaterThread = true, bool ToDeleteLaterObject = true)		// запуск нового потока
	{
		_finished_signal = FinishedSignal;		// запоминание имени сигнала "окончание работы объекта"
		_terminate_slot = TerminateSlot;		// запоминание имени слота "остановка работы"
		_to_delete_later_object = ToDeleteLaterObject;	// запоминание установки отложенного удаление объекта
		start (Priority);	// создание объекта
	}

	void run (void)	{ initObject (_obj = new T); exec (); }		// создание объекта

	// . состояние
	bool objectIsCreated (void) const	{ return _obj != 0; }							// объект готов к работе?

	T* ptr (void) 						{ return reinterpret_cast <T*> (_obj); }			// указатель на объект
	const T* cptr (void) const			{ return reinterpret_cast <const T*> (_obj); }		// указатель на константный объект

	// . перегрузки
	operator T* (void)					{ return ptr (); }		// указатель на объект
	T* operator -> (void)				{ return ptr (); }		// указатель на объект
	operator const T* (void) const		{ return cptr (); }		// указатель на константный объект
	const T* operator -> (void) const	{ return cptr (); }		// указатель на константный объект
};	// class ThreadedObject

Файл main.cpp:

#include <QtGui>
#include <QtWidgets>
#include <QtCore>

#include "ThreadedObject.h"

// **
// **  Выполнение операции
// **

class Operation: public QObject
{
	Q_OBJECT

	int		*Int;		// некоторая динамическая переменная
	QTimer	_tmr;		// таймер
	int		_int_timer;	// внутренний таймер

public:
	Operation (void)	{ Int = new int (5); }			// определенный конструктор
	~Operation (void)	{ if (Int) delete Int; }		// определенный деструктор

signals:
    void addText(const QString &txt);		// сигнал "добавление текста"
	void finished ();						// сигнал "остановка работы"

public slots:
	void terminate ()			// досрочная остановка
	{
		killTimer (_int_timer);		// остановка внутреннего таймера
		_tmr.stop ();				// остановка внешенго таймера
		delete Int;					// удаление переменной
		Int = 0;					// знак заключения работы
		emit finished ();			// сигнал завергения работы
	}
    void doAction (void)		// некоторое действие
    {
		bool res;
		emit addText (QString ("- %1 -"). arg (*Int));
		res = QObject::connect (&_tmr, &QTimer::timeout, this, &Operation::timeout);	Q_ASSERT_X (res, "connect", "connection is not established");	// связывание внешнего таймера
		_tmr.start (2000);			// запуск внешнего таймера
		thread()->sleep (1);		// выжидание 1 сек...
		timeout ();					// ... выдача состояния ...
		startTimer (2000);			// ... и установка внутреннего таймера
    }
protected:
	void timerEvent (QTimerEvent *ev)	{ timeout (); }		// внутренний таймер

private slots:
	void timeout (void)
	{
		if (!Int || !*Int)									// поток закрывается?
			return;											// ... выход
		--*Int;												// уменьшение счетчика
		emit addText (QString ("- %1 -"). arg (*Int));		//выдача значения

		if (!Int || !*Int)									// таймер закрыт?
			emit finished ();								// ... выход

	}

};

// **
// **  Объект, взаимодействующий с потоком
// **

class App: public QObject
{
	Q_OBJECT

	ThreadedObject <Operation>	_obj;		// объект-поток
	QPushButton _btn;						// кнопка

protected:
	void timerEvent (QTimerEvent *ev)
	{
		bool res;							// знак успешности установки сигналов-слотов
		killTimer (ev->timerId ());			// остановка таймера
		res = QObject::connect (&_obj, SIGNAL (objectIsReady ()), this, SLOT (connectObject ()));		Q_ASSERT_X (res, "connect", "connection is not established");	// установка связей с объектом
		_obj.starting (SIGNAL (finished ()), SLOT (terminate ()), QThread::HighPriority);					// запуск потока с высоким приоритетом
	}

private slots:
	void setText (const QString &txt)		{ _btn.setText (txt); }		// установка надписи на кнопке
	void connectObject (void)		// установка связей с объектом
	{
		bool res;					// знак успешности установки сигналов-слотов
		res = QObject::connect (this, &App::finish, _obj, &Operation::terminate);				Q_ASSERT_X (res, "connect", "connection is not established");	// закрытие этого объекта хакрывает объект в потоке
		res = QObject::connect (this, &App::startAction, _obj, &Operation::doAction);			Q_ASSERT_X (res, "connect", "connection is not established");	// установка сигнала запуска действия
		res = QObject::connect (_obj, &Operation::finished, this, &App::finish);				Q_ASSERT_X (res, "connect", "connection is not established");	// конец операции завершает работу приложения
		res = QObject::connect (_obj, &Operation::addText, this, &App::setText);				Q_ASSERT_X (res, "connect", "connection is not established");	// установка надписи на кнопку
		res = QObject::connect (&_btn, &QPushButton::clicked, _obj, &Operation::terminate);		Q_ASSERT_X (res, "connect", "connection is not established");	// остановка работы потока

		_btn.show ();				// итог кнопки
		emit startAction ();		// запуск действия
	}

public slots:
	void terminate (void)			{ emit finish (); }		// заключение работы приложения

signals:
	void startAction (void);		// сигнал "запуск действия"
	void finish (void);				// сигнал "заключение работы"
};

// **
// **  Точка входа в программу
// **

int main (int argc, char **argv)
{
    QApplication app (argc, argv);		// приложение
	App a;								// объект
	bool res;							// знак успешности операции

	a.startTimer (0);					// вызов функции таймера объекта при включении цикла обработки сообщений
	res = QObject::connect (&a, SIGNAL (finish ()), &app, SLOT (quit ()));				Q_ASSERT_X (res, "connect", "connection is not established");	// окончание работы объекта закрывает приложение
	res = QObject::connect (&app, SIGNAL (lastWindowClosed ()), &a, SLOT (terminate ()));	Q_ASSERT_X (res, "connect", "connection is not established");	// окончание работы приложения закрывает объект

	return app.exec();					// запуск цикла обработки сообщений
}

#include "main.moc"

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB