У меня есть живой объект, реализованный следующим образом. Он используется для выполнения длинных задач в фоновом режиме. Основной поток вызывает задачи путем отправки сигнала в общедоступные слоты (т.е. DoTask). Ниже приведен пример (не проверен).
class MyTask : public QObject
{
Q_OBJECT
public:
MyTask();
~MyTask();
public slots:
void doTask( int param );
private slots:
void stated();
signals:
void taskCompleted( int result );
private:
QThread m_thread;
};
MyTask::MyTask()
{
moveToThread(&m_thread);
connect( &m_thread, SIGNAL(started()), this, SLOT(started()));
m_thread.start();
}
MyTask::~MyTask()
{
// Gracefull thread termination (queued in exec loop)
if( m_thread.isRunning() )
{
m_thread.quit();
m_thread.wait();
}
}
void MyTask::started()
{
// initialize live object
}
void MyTask::doTask( int param )
{
sleep( 10 );
emit taskCompleted( param*2 );
}
Это (должно) работать как ожидалось, пока doTask() вызывается сигналом. Но если основной поток вызывает doTask() напрямую, он будет выполняться основным потоком. Для некоторых задач я хочу принудительно выполнить выполнение потоком живого объекта, даже если метод слота вызывается напрямую.
Я мог бы добавить код перед doTask(), чтобы проверить, является ли текущий поток m_thread, в этом случае он выполняет этот метод. Если нет, мне бы хотелось, чтобы doTask() выдавал сигнал на 'this', так что вызов doTask() помещается в очередь в цикле exec m_thread и выполняется как можно скорее.
Как я мог это сделать?
РЕДАКТИРОВАТЬ. На основе предлагаемого ответа, вот новый код. Метод doTask теперь делегирует выполнение потоком live objet, даже если он вызван непосредственно основным потоком. Вызывается сигналом по-прежнему работает как ожидалось.
class MyTask : public QObject
{
Q_OBJECT
public:
explicit MyTask( QObject *parent = 0 );
~MyTask();
public slots:
void doTask( int param );
private slots:
void doTaskImpl( int param );
signals:
void taskCompleted( int result );
private:
QThread m_thread;
};
MyTask::MyTask( QObject *parent) : QObject(parent)
{
moveToThread(&m_thread);
m_thread.start();
}
MyTask::~MyTask()
{
// Gracefull thread termination (queued in exec loop)
if( m_thread.isRunning() )
{
m_thread.quit();
m_thread.wait();
}
}
void MyTask::doTask( int param )
{
QMetaObject::invokeMethod( this, "doTaskImpl", Q_ARG( int, param ) );
}
void MyTask::doTaskImpl( int param )
{
// Do the live oject asynchronous task
sleep( 10 );
emit taskCompleted( param*2 );
}
Это самая простая реализация, которую я смог найти для поддержки выполнения асинхронных методов в отдельном потоке. Вызовы методов doTask() будут поставлены в очередь и обработаны, как только начнется поток. При вызове из потока объектов он будет выполнен немедленно (не в очереди).
Обратите внимание, что сигнал start() испускается только при запуске потока. Это означает, что вызов метода doTask(), поставленный перед запуском потока, будет выполняться до вызова слота метода start(). Именно по этой причине я удалил его из первоначальной реализации. Таким образом, инициализация объекта предпочтительно должна выполняться в конструкторе.