在没有连接的情况下调用槽方法?

发布于 2024-09-10 22:00:58 字数 2247 浏览 3 评论 0原文

我有一个通过以下方式实现的活动对象。它用于在后台执行长时间任务。主线程通过向公共槽发送信号(即 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() 的调用在 m_thread exec 循环中排队并尽快由它执行。

我怎么能这么做呢?

编辑:根据建议的答案,这是新代码。 doTask 方法现在委托活动对象的线程执行,即使是由主线程直接调用也是如此。通过信号调用仍然按预期工作。

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

这是我能找到的支持在单独线程中执行异步方法的最简单的实现。 doTask() 方法的调用将在线程启动后立即排队并进行处理。当从对象线程调用时,它将立即执行(不排队)。

请注意,started() 信号仅在线程启动时发出。这意味着在线程启动之前排队的 doTask() 方法调用将在调用started() 方法槽之前执行。这就是我从最初的实现中删除它的原因。因此,对象初始化最好在构造函数中执行。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦在夏天 2024-09-17 22:00:58

您想调用 QMetaObject::invokeMethod来做到这一点。在你的情况下,它看起来像

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

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 ) );
記柔刀 2024-09-17 22:00:58

我要添加的唯一改进是节省查找方法的时间:

class MyTask {
// ...
private:
  int m_doTaskImplIndex;
};

MyTask::MyTask() :
  //...
  m_doTaskImplIndex(metaObject()->indexOfMethod("doTaskImpl"))
  //...
{}

void MyTask::doTask( int param )
{
  metaObject()->method(m_doTaskImplIndex).invoke(this, Q_ARG( int, param ) );
}

About the only improvement I'd add is to save some time on looking up the method:

class MyTask {
// ...
private:
  int m_doTaskImplIndex;
};

MyTask::MyTask() :
  //...
  m_doTaskImplIndex(metaObject()->indexOfMethod("doTaskImpl"))
  //...
{}

void MyTask::doTask( int param )
{
  metaObject()->method(m_doTaskImplIndex).invoke(this, Q_ARG( int, param ) );
}
顾忌 2024-09-17 22:00:58

我怀疑 MyTask 存在错误。如果我正确理解了 Qt 内部结构,那么


moveToThread(&m_thread);
将失败。

如果 parent 不为 0,

I suspect there is a bug in MyTask. If I've understood Qt internals correctly then


moveToThread(&m_thread);

will fail if parent isn't 0.

忱杏 2024-09-17 22:00:58

那么,将它们全部包装到一个漂亮的类中怎么样?
我还添加了一个槽 finishPlease,它将作为消息待办事项列表中的最后一个元素添加,并在主程序实际处理完所有待处理消息后才可以终止它,并向主程序提供反馈。

class Threaded : public QObject
{
    Q_OBJECT
public:
    Threaded() {
        thread = new QThread(this);
        this->moveToThread(thread);
        connect(thread, SIGNAL(started()), this, SLOT(init()), \
                                                  Qt::QueuedConnection);
        thread->start();
    }

    virtual ~Threaded() {
        thread->exit();
        thread->wait();
        delete thread;
    }

signals:
    void okayKillMe();

public slots:
    virtual void init() = 0;
    void finishPlease() {emit okayKillMe();}

protected:
    QThread* thread;
};

class MyClass : public Threaded
{
  Q_OBJECT
public:
    MyClass() { }
    virtual ~MyClass() { }

public slots:
    void init() { }
    void doStuff() { }
    void doOtherStuff(int* data) { }

};

So, how about wrapping it all into a nice class?
I have also added a slot finishPlease, which will be added as last element in the message todo list, and gives feedback to the main program when it has actually processed all pending messages before it can be killed.

class Threaded : public QObject
{
    Q_OBJECT
public:
    Threaded() {
        thread = new QThread(this);
        this->moveToThread(thread);
        connect(thread, SIGNAL(started()), this, SLOT(init()), \
                                                  Qt::QueuedConnection);
        thread->start();
    }

    virtual ~Threaded() {
        thread->exit();
        thread->wait();
        delete thread;
    }

signals:
    void okayKillMe();

public slots:
    virtual void init() = 0;
    void finishPlease() {emit okayKillMe();}

protected:
    QThread* thread;
};

class MyClass : public Threaded
{
  Q_OBJECT
public:
    MyClass() { }
    virtual ~MyClass() { }

public slots:
    void init() { }
    void doStuff() { }
    void doOtherStuff(int* data) { }

};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文