原理上QT是如何实现线程间通信中断当前任务的呢?

吾心安处方是家 2016-05-12 11:28:57
加精
为了描述的的问题, 我写了一个比较完整的有针对性的例子. 用的是qt4.8

pro文件
TEMPLATE    = app

QT += core network
QT -= gui

TARGET = TEST

HEADERS += myobject.h
SOURCES += myobject.cpp


myobject.h
#ifndef _MY_OBJECT_H_
#define _MY_OBJECT_H_

#include <QEvent>
#include <QTcpSocket>
#include <QTcpServer>
#include <QThread>
#include <QDebug>
#include <QObject>
#include <QTimer>
#include <QObject>
#include <QHostAddress>

const QEvent::Type TYPE_ADDR = (QEvent::Type)5001;
const QEvent::Type TYPE_ITC = (QEvent::Type)5002;


struct ConnectAddr : public QEvent
{
ConnectAddr(const QHostAddress &ip,
quint16 port)
: QEvent(TYPE_ADDR),
m_ip(ip),
m_port(port) {}
const QHostAddress m_ip;
const quint16 m_port;
};

struct ITC : public QEvent
{
ITC(const QString& data)
: QEvent(TYPE_ITC),
m_data(data) {}
const QString m_data;
};

class MyObject : public QObject
{
Q_OBJECT
public:
MyObject();

protected Q_SLOTS:
void Started();
void TriggerNewConnection();
void TriggerDataReceived();

protected:
virtual void customEvent(QEvent *e);
void Connect(const QHostAddress &connectAddr,
quint16 nConnectPort);

private:
QTcpServer *m_pTcpServer;
};


#endif // _MY_OBJECT_H_



myobject.cpp
#include <QCoreApplication>
#include "myobject.h"

MyObject::MyObject():m_pTcpServer(NULL)
{
}

void MyObject::Started()
{
qDebug() << QString("[%1]Started")
.arg((qlonglong)QThread::currentThreadId());
}

void MyObject::Connect(const QHostAddress &connectAddr,
quint16 nConnectPort)
{
if(!m_pTcpServer)
{
m_pTcpServer = new QTcpServer(this);
connect(m_pTcpServer, SIGNAL(newConnection()), this , SLOT(TriggerNewConnection()));
}

if(!m_pTcpServer->listen(connectAddr, nConnectPort))
{
delete m_pTcpServer;
m_pTcpServer = NULL;
}
}

void MyObject::customEvent(QEvent *e)
{
switch(e->type())
{
case TYPE_ADDR:
{
ConnectAddr *addr = static_cast<ConnectAddr*>(e);
qDebug() << QString("[%1]catch the event: connect-addr(%2 %3)")
.arg((qlonglong)QThread::currentThreadId())
.arg(addr->m_ip.toString())
.arg(addr->m_port);
Connect(addr->m_ip, addr->m_port);
}
break;
case TYPE_ITC:
{
ITC *itc = static_cast<ITC*>(e);
qDebug() << QString("[%1]catch the event: itc(%2)")
.arg((qlonglong)QThread::currentThreadId())
.arg(itc->m_data);
}
break;
default:
break;
}
}

void MyObject::TriggerNewConnection()
{
QTcpSocket *socket = m_pTcpServer->nextPendingConnection();
if(NULL == socket)
{
qDebug() << QString("[%1]TriggerNewConnection: Socket is null")
.arg((qlonglong)QThread::currentThreadId());
return ;
}

connect(socket, SIGNAL(readyRead()), this, SLOT(TriggerDataReceived()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));

qDebug() << QString("[%1]TriggerNewConnection: %2 %3")
.arg((qlonglong)QThread::currentThreadId())
.arg(socket->peerAddress().toString())
.arg(socket->peerPort());
}

void MyObject::TriggerDataReceived()
{
QTcpSocket* socket = dynamic_cast<QTcpSocket*>(sender());
QByteArray data = socket->readAll();
qDebug() << QString("[%1]TriggerDataReceived: %2")
.arg((qlonglong)QThread::currentThreadId())
.arg(QString::fromUtf8(data));
}


int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyObject* myObj = new MyObject;

QThread *thread = new QThread();
myObj->moveToThread(thread);
thread->start();

QObject::connect(thread, SIGNAL(started()), myObj, SLOT(Started()), Qt::QueuedConnection);

QCoreApplication::postEvent(myObj, new ConnectAddr(QHostAddress::Any, 12345));

for(qint32 i = 0; ; i++) {
QEventLoop EventLoop;
QTimer::singleShot(3*1000, &EventLoop, SLOT(quit()));
EventLoop.exec();

QCoreApplication::postEvent(myObj, new ITC(QString("I have been running %1 times.").arg(i)));
}

return a.exec();
}




代码不存在问题.

myobject全在子线程执行任务.
主线程是main函数.
以实现2个线程,主线程负责发布新任务. 子线程负责运行具体的既有任务.

问题来了1. 当前例子中. 子线程是如何既随时接收主线程的itc消息(消息结构ITC). 又随时接收远程tcp数据(槽函数TriggerDataReceived)的呢?

[猜测] 在线程中绑定一个管道专门用于线程间通信, QCoreApplication::postEvent则会向管道中写入数据, 而后通过select/poll监听到有读时间. 同样, tcp读事件也是通过select/poll监听.

问题来了2. qt这种模式是不是很好呢? qt是否是因为对象和线程的强绑定关系才使用这种模式的呢? 像别的库,boost/zeromq这些,似乎对线程和对象完全没有绑定关系. 导致如boost::asio::io_service或者zmq的海盗模型, 什么对象运行在什么线程是不固定的. 那我要通知某个特定对象执行某个特定任务似乎变得很不方便(小弟不才,就我所知boost::asio::io_service::post是可以实现类似功能, 但是我认为需要加锁; zeromq似乎无解.), 还是是否我思路有问题.

问的东西比较乱. 大家各抒己见吧. 也欢迎大婶来吐槽 - -
...全文
3324 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_33694889 2016-08-25
  • 打赏
  • 举报
回复
66666666
赵4老师 2016-08-18
  • 打赏
  • 举报
回复
《Windows核心编程》
赵4老师 2016-08-18
  • 打赏
  • 举报
回复
《Windows何尝不想》
kongl123 2016-08-17
  • 打赏
  • 举报
回复
引用 9 楼 CCDDzclxy 的回复:
为何 5月份的,到 8月份 才顶起..................
因为我是新来的,看到了就顶起了
CCDDzclxy 2016-08-17
  • 打赏
  • 举报
回复
为何 5月份的,到 8月份 才顶起..................
u011036011 2016-08-13
  • 打赏
  • 举报
回复
阁下何不同风起,扶摇直上九万里
hugh_z 2016-08-13
  • 打赏
  • 举报
回复
6666666
cattpon 2016-08-13
  • 打赏
  • 举报
回复
表示看不懂呢~
ljheee 2016-08-12
  • 打赏
  • 举报
回复
kongl123 2016-08-12
  • 打赏
  • 举报
回复
主要是希望知道哪个任务(或者某个公共函数)是哪个对象执行的,如果不用线程来识别也没有特别好的方法来代替实现
kongl123 2016-08-12
  • 打赏
  • 举报
回复
我想到的也是把对象和线程绑定,然后通过共享一个任务池(我是自己实现了一个,容易改,你说用系统缓存也没问题),绑定后就不会找不到北了。我想的绑定是可以根据需求,在不同时间更换绑定(1绑n的情况,这样使任务池的无锁成为可能),解绑之类的。实时的关系可以通过一个关联类存储和查询。
赵4老师 2016-05-12
  • 打赏
  • 举报
回复
IDE中,在不明白的符号上点鼠标右键,选转到定义。

5,530

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 模式及实现
社区管理员
  • 模式及实现社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧