tags:

views:

273

answers:

1

I have a live object implemented in the following way. It is used to execute long tasks in background. The main thread invoke the tasks by sending a signal to the public slots (i.e. doTask). Here is a stripped down example (not tested).

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 );
}

This (should) work as expected as long as doTask() is invoked by a signal. But if the main thread calls doTask() directly it will then be executed by the main thread. For some tasks, I want to enforce an execution by the live object's thread, even if the slot method is called directly.

I could add code in front of doTask() to check if the current thread is m_thread in which case it executes the method. If not I would like that doTask() emits a signal to 'this' so that an invocation of doTask() is queued in the m_thread exec loop and executed by it as soon as possible.

How could I do that ?

EDIT: Based on the proposed answer, here is the new code. The doTask method now delegates execution by the live objet's thread, even if called directly by the main thread. Called by signal still works as expected.

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's asynchronous task
    sleep( 10 );
    emit taskCompleted( param*2 );
}

This is the most simple implementation I could find to support asynchronous method executions in a separate thread. The invocations of the doTask() methods will be queued and processed as soon as the thread is started. When called from the object thread, it will be executed immediately (not queued).

Note that the started() signal is emitted only when the thread is started. This means that doTask() method invocation queued before the thread is started will execute before the started() method slot is invoked. This is the reason I removed it from the initial implementation. Object initialization should thus preferably be performed in the constructor.

+2  A: 

You want to call QMetaObject::invokeMethod to do this. In your case, it would look something like

MyTask *task;
int param;
// ...
// Will automatically change threads, if needed, to execute 
// the equivalent of:
// (void)task->doTask( param );
QMetaObject::invokeMethod( task, "doTask", Q_ARG( int, param ) );
Caleb Huitt - cjhuitt
This is awesome. It works like a charm.See my Edit for used code.
chmike