C++ 与QTimer和QTcpSocket一起正确使用QThread和moveToThread
通过阅读本文和其他一些文章,子类化QThread是一种糟糕的做法。所以我试着用这个方法 但我的问题是,我想转移到另一个线程的类中有一个QTimer和一个qtcsocket。突然间,这并不像所用的例子那么简单( 这里是CommsHandlerIP类,不包括方法C++ 与QTimer和QTcpSocket一起正确使用QThread和moveToThread,c++,multithreading,qt,qthread,C++,Multithreading,Qt,Qthread,通过阅读本文和其他一些文章,子类化QThread是一种糟糕的做法。所以我试着用这个方法 但我的问题是,我想转移到另一个线程的类中有一个QTimer和一个qtcsocket。突然间,这并不像所用的例子那么简单( 这里是CommsHandlerIP类,不包括方法 class CommsHandlerIP : public QObject { Q_OBJECT public: CommsHandlerIP(); ~CommsHandlerIP(void); protected:
class CommsHandlerIP : public QObject
{
Q_OBJECT
public:
CommsHandlerIP();
~CommsHandlerIP(void);
protected:
QTcpSocket m_TCPSocket;
QTimer m_timer;
}
问题是,即使移动了CommsHandlerIP,QTimer和qtcsocket(在CommsHandlerIP类内)也在主线程中。因此,我无法启动计时器或连接套接字
如果我尝试移动到QTimer和qtcsocket的线程(例如,在构造函数中通过传递线程指针),当我离开应用程序时,这会变得非常混乱
我该怎么办?类实例是在调用线程上创建的。
QTimer
继承QObject
。
如果Qt
上的每个线程调用exec()
,则它可以有一个事件循环。
因此,您希望将QTimer
移动到另一个线程上的事件循环。
所以你应该手动移动它
因此,将其创建延迟到移动对象之后:-
class CommsHandlerIP : public QObject
{
Q_OBJECT
public slots:
void Initialise();
private:
void Run();
// c++ 11, initialising in headers...
QTimer* m_pTimer = NULL;
QTcpSocket* m_pSocket = NULL;
};
void CommsHandlerIP::Initialise()
{
m_pTimer = new QTimer(this);
m_pSocket = new QTcpSocket(this);
Run();
}
QThread m_commsThread;
m_pICommsHandler = new CommsHandlerIP();
// Note Qt 5 connect style
connect(&m_commsThread, &QThread::started, m_pICommsHandler, &CommsHandlerIP::Initialise);
m_pICommsHandler->moveToThread(&m_commsThread);
m_commsThread.start();
当线程启动时,调用
CommsHanderIP
初始化函数;在调用Run()之前,您应该在这里创建和设置qtcsocket
和QTimer
对象
。由于在创建这些对象之前,CommsHandlerIP
正在新线程中运行,因此它们也将共享相同的线程亲缘关系。有一种更简单的方法来实现这一切,它遵循相同的算法,但不涉及创建线程和更改线程亲缘关系所需的所有样板文件,使用钕
如果我转换Merlin069的示例,您将看到它如何简化代码:
class CommsHandlerIP : public QObject, public QRunnable
{
Q_OBJECT
public:
void run();
public slots:
//... any slots
signals:
//... any signals
private:
// c++ 11, initialising in headers...
QTimer* m_pTimer = NULL;
QTcpSocket* m_pSocket = NULL;
};
void CommsHandlerIP::run()
{
m_pTimer = new QTimer();
m_pSocket = new QTcpSocket();
//...
delete m_pTimer;
delete m_pSocket;
}
QThreadPool::globalInstance()->start(new CommsHandlerIP);
我在搜索计时器行为和movetoThread时偶然发现了这一点。 接受的答案是一个很好的解决方法,但实际上并不是问题的根本原因。有一个一般规则,当您移动一个对象时,所有子对象也会移动。因此,您只需确保QTimer成为子对象,以便在其构造函数中传递this指针
CommsHandlerIPL::CommsHandlerIP()
: QObject(), m_pTimer(new QTimer(this)) // <=== crucial to make it a "child" object
{
}
CommsHandlerIP::CommsHandlerIP()
:QObject(),m_pTimer(new QTimer(this))//我觉得自己很愚蠢,我知道问题出在哪里,但我试图以完全错误的方式解决它。当你知道如何解决问题时,这很容易;O)在构造函数中创建它们时,将this
作为m_TCPSocket
和m_timer
的父对象传递就足够了。这是因为更改了此对象及其子对象的线程关联性。因此,您不必延迟它们的创建。是的,但是如果在移动信号/插槽之前已经与它们建立了连接,那么问题可能会在以后发生。创建的连接类型取决于调用方和被调用方的线程亲缘关系。@Merlin069 AFAIK,如果类型是Qt::AutoConnection
,则在调用moveToThread()
之前是否建立连接并不重要。这是因为如果信号是从与接收对象不同的线程发出的,则信号将排队,表现为Qt::QueuedConnection。否则,将直接调用插槽,其行为类似于Qt::DirectConnection。连接类型在发出信号时确定
。
CommsHandlerIPL::CommsHandlerIP()
: QObject(), m_pTimer(new QTimer(this)) // <=== crucial to make it a "child" object
{
}