Qt信号/槽和pthread不兼容

3
有人能告诉我为什么这段QT代码在定义ASYNC_TIMERS时不会调用回调函数(即从pthread中调用m_timer.start,但槽函数从未运行)吗?显然,由于它是从pthread中调用的,所以当ASYNC_TIMERS未被定义时它可以工作,但我想知道如何从pthread中修复它。我尝试了许多在网上找到的方法,包括moveToThread(),调用线程运行调用exec(),但在这个问题上我没有运气。 乐福彩票网

干杯

multitimer.h:

#pragma once

#ifndef MULTI_TIMER_H
#define MULTI_TIMER_H

#include <QThread>
#include <QTimer>
#include <QMutex>

#include <QMap>


#include <QMetaType>
#include <cassert>


class MultiTimer : public QThread
{
    Q_OBJECT

public:
    typedef void (*multiTimerCallback)(quint32 p_id);

private:
    QTimer m_timer;
    QMutex m_mutex;
    quint32 m_id;
    multiTimerCallback m_callback;
    void KillTimer(void);

public:
    // only TimerFactory is allowed to instantiate MultiTimer
    MultiTimer(quint32 p_id, multiTimerCallback p_callback);
    ~MultiTimer();
    enum TTimerType
    {
        TT_SingleShot,      ///< Timer fires only once
        TT_Repetitive       ///< Timer keeps firing repeatedly until stopped with KillTimer()
    };
    void SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType);

private slots:
    void Update(void);
};

#endif

timer.cpp:

#include <QtCore/QCoreApplication>
#include "multitimer.h"
#include <stdio.h>


//--------------------------------------------------------------------------------------------------

void MultiTimer::SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType)
{
    QMutexLocker locker(&m_mutex);

    m_timer.setSingleShot(TT_SingleShot == timerType ? true : false);
    m_timer.start(p_delayInMilliseconds);
    //QTimer::singleShot(p_delayInMilliseconds, this,SLOT(Update()));
}

void MultiTimer::KillTimer(void)
{
    QMutexLocker locker(&m_mutex);
    m_timer.stop();
}

void MultiTimer::Update(void)
{
    QMutexLocker locker(&m_mutex);

    if (NULL != m_callback)
        m_callback(m_id);
}

MultiTimer::MultiTimer(quint32 p_id, multiTimerCallback p_callback)
    : m_id(p_id)
    , m_callback(p_callback)
{
    bool isConnected = true;
    isConnected &= this->connect(&this->m_timer, SIGNAL(timeout()), this, SLOT(Update()), Qt::QueuedConnection);
    assert(isConnected);
    //this->start();
}

MultiTimer::~MultiTimer()
{
    KillTimer();
    wait();
}


//--------------------------------------------------------------------------------------------------
#define ASYNC_TIMERS
#define GLOBAL_TIMERS

void Callback(quint32 p_id)
{
    printf("Got timered by timer %d.\n", p_id);
}

MultiTimer *mt;
void StartTimers(void)
{
    #ifndef GLOBAL_TIMERS
    mt = new MultiTimer(1, Callback);
    #endif
    mt->SetTimer(1000, MultiTimer::TT_SingleShot);
}

#ifdef ASYNC_TIMERS
pthread_t AsyncTaskThread;
void *ProcessAsyncTasks(void */*ptr*/)
{
    StartTimers();
    return NULL;
}
#endif

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    #ifdef GLOBAL_TIMERS
    mt = new MultiTimer(1, Callback);
    #endif

    #ifdef ASYNC_TIMERS
    pthread_create(&AsyncTaskThread, NULL, &ProcessAsyncTasks, NULL);
    #else
    StartTimers();
    #endif

    return a.exec();
}

你同时使用 GLOBAL_TIMERS 和 ASYNC_TIMERS 吗? - 0xbaadf00d
3个回答

4
我认为线程和 QObjects有答案:不能在与创建对象的线程不同的线程中使用事件驱动对象。
在您的代码中,如果启用了GLOBAL_TIMERS,则将在主线程中创建MultiTimer,但在另一个线程中调用m_timer.start()
引用文档:

只能在单个线程中使用基于事件的对象。特别是,这适用于计时器机制和网络模块。例如,在不是对象所在线程的线程中无法启动计时器或连接套接字。

所以不要那样做。(顺便使用QThread。)

@justanothercoder:使用GLOBAL_TIMERS,mt在主线程中创建,然后如果定义了ASYNC_TIMERS,则在新创建的普通pthread中调用StartTimers。 - Mat
@justanothercode:在那段代码片段中有 #define ASYNC_TIMERS#define GLOBAL_TIMERS - Mat
@justanothercoder:在start timer中的new MultiTimer只有在GLOBAL_TIMERS定义的情况下才会出现。 - Mat
啊,你说得对 (+1),我还在想两者不能同时定义。但是,我认为即使只定义了ASYNC_TIMERS也不会起作用,因为在创建QObject派生对象的线程中没有事件循环。 - 0xbaadf00d
@justanothercoder:是的,你说得对。事件循环是必要的,他需要更加小心地处理对象的初始线程。 - Mat
显示剩余9条评论

1
你需要一个 QEventLoop 在新线程中处理信号/槽。

QTimer 需要这些来工作。

void *ProcessAsyncTasks(void */*ptr*/)
{    
    QEventLoop loop;
    StartTimers();    
    loop.exec();
    return NULL; 
}

为什么不使用QThread?


我会尝试一下,但对于我们的最终设计来说并不是很可行,因为我们从pthread的外部库获取事件。我曾尝试使用multitimers运行方法(因为它是一个线程)来调用exec,但那并没有起作用。 - othane
我认为你应该再仔细看看这些类是做什么的,需要什么才能运行,然后你会找到更好的答案。如果不知道你想要达成什么目标,我很难找到有用的帮助。 - 0xbaadf00d

0

对于任何试图解决这个问题的人,我想我已经找到了解决方法。如果我将m_timer和multitimer对象移回主qt线程并“正确地”发出计时器启动信号,似乎一切都能正常工作(见下文)。

可能我仍然可以在qthread中进行exec调用以接管pthread并将multitimer和m_timer移动到其中,但是现在这种方式可以正常工作,并且即使pthread死亡后我也能按时获得“Got timered by timer %d.\n”输出,这正是我想要的。

感谢大家的建议,如果有更好的方法或者我忽略了什么重大错误,那就太好了?谢谢

multitimer.h:

/**
    @file multitimer.h
    @brief Partial implementation of Windows-like SetTimer()/KillTimer() for Qt.

*/
#pragma once

#ifndef MULTI_TIMER_H
#define MULTI_TIMER_H

#include <QThread>
#include <QTimer>
#include <QMutex>

#include <QMap>


#include <QMetaType>
#include <cassert>


class MultiTimer : public QThread
{
    Q_OBJECT

public:
    typedef void (*multiTimerCallback)(quint32 p_id);

private:
    QTimer m_timer;
    QMutex m_mutex;
    quint32 m_id;
    multiTimerCallback m_callback;
    void KillTimer(void);

public:
    // only TimerFactory is allowed to instantiate MultiTimer
    MultiTimer(quint32 p_id, multiTimerCallback p_callback);
    ~MultiTimer();
    enum TTimerType
    {
        TT_SingleShot,      ///< Timer fires only once
        TT_Repetitive       ///< Timer keeps firing repeatedly until stopped with KillTimer()
    };
    void SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType);

private slots:
    void Update(void);

signals:
    void start_sig(int);
};

#endif    

timer.cpp:

#include <QtCore/QCoreApplication>
#include "multitimer.h"
#include <stdio.h>


//--------------------------------------------------------------------------------------------------

void MultiTimer::SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType)
{
    QMutexLocker locker(&m_mutex);
    m_timer.setSingleShot(TT_SingleShot == timerType ? true : false);
    connect(this, SIGNAL(start_sig(int)), &m_timer, SLOT(start(int)), Qt::QueuedConnection);
    //m_timer.start(p_delayInMilliseconds);
    emit start_sig(p_delayInMilliseconds);
    disconnect(this, SIGNAL(start_sig(int)), &m_timer, SLOT(start(int)));
}

void MultiTimer::KillTimer(void)
{
    QMutexLocker locker(&m_mutex);
    m_timer.stop();
}

void MultiTimer::Update(void)
{
    QMutexLocker locker(&m_mutex);

    if (NULL != m_callback)
        m_callback(m_id);
}

MultiTimer::MultiTimer(quint32 p_id, multiTimerCallback p_callback)
    : m_id(p_id)
    , m_callback(p_callback)
{
    bool isConnected = true;
    isConnected &= this->connect(&this->m_timer, SIGNAL(timeout()), this, SLOT(Update()), Qt::QueuedConnection);
    assert(isConnected);
    //this->start();

    moveToThread(qApp->thread());
    m_timer.moveToThread(qApp->thread());
}

MultiTimer::~MultiTimer()
{
    KillTimer();
    wait();
}


//--------------------------------------------------------------------------------------------------
#define ASYNC_TIMERS
#define xGLOBAL_TIMERS

void Callback(quint32 p_id)
{
    printf("Got timered by timer %d.\n", p_id);
}

MultiTimer *mt;
void StartTimers(void)
{
    #ifndef GLOBAL_TIMERS
    mt = new MultiTimer(1, Callback);
    #endif
    mt->SetTimer(2000, MultiTimer::TT_SingleShot);
}

#ifdef ASYNC_TIMERS
pthread_t AsyncTaskThread;
void *ProcessAsyncTasks(void */*ptr*/)
{
    StartTimers();
    return NULL;
}
#endif

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    #ifdef GLOBAL_TIMERS
    mt = new MultiTimer(1, Callback);
    #endif

    #ifdef ASYNC_TIMERS
    pthread_create(&AsyncTaskThread, NULL, &ProcessAsyncTasks, NULL);
    #else
    StartTimers();
    #endif

    return a.exec();
}

你想要实现什么?除非我知道你想要的是什么,否则我无法帮助你。如果你只需要一个在某个时间间隔内调用某个函数的 QTimer,那么根本不需要为此创建线程。如果你想让 QTimers 在不同的线程中运行,你需要在这些线程中运行 QEventLoop。这就是你在主线程中拥有的,因为 QCoreApplication 提供了它。 - 0xbaadf00d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接