使用 Qt 提升 asio
Posted
技术标签:
【中文标题】使用 Qt 提升 asio【英文标题】:Boost asio with Qt 【发布时间】:2017-01-21 16:57:55 【问题描述】:我正在尝试将boost::asio async client example 与简单的 Qt GUI 一起使用,例如:
我的应用程序中的一点 sn-p:
按钮点击SLOT:
void RestWidget::restGetCall()
networkService ntwkSer("www.boost.org","80");
connect(&ntwkSer, SIGNAL(responseReady(std::string)), this, SLOT(showResponse(std::string)));
ntwkSer.get("/LICENSE_1_0.txt");
networkService类只是上面链接的boost示例代码的一个包装器。它派生自QObject类,用于信号、槽机制。
void networkService::get(const std::string & path)
// boost::thread (boost::bind(&networkService::networkCall,this,path));//this gives me sigabrt
networkCall(path);//this works fine, and i get output as pictured above.
void networkService::networkCall(const std::string path)
tcp::resolver::query query(host_, port_);//also these host,port fields come out to be invalid/garbage.
//tcp::resolver::query query("www.boost.org","80");//still doesnt resolve the SIGABRT
resolver_.async_resolve(query,
boost::bind(&networkService::handle_resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator,
path));
io_service.run();
问题是当我从 boost::thread 运行 io_service.run() 时。我得到了 SIGABRT。
还有他在networkService::networkCall(path)函数中的host_,port_networkService包装类字段在调试时是无效的,在构造时被保存:
networkService ntwkSer("www.boost.org","80");
boost::thread 的明显原因是让 GUI 非阻塞,因为 io_service() 有自己的事件循环。我的意图是在单独的 boost 线程中运行 boost::asio 异步调用,并使用 QT 的 Q_OBJECT 信号槽机制通知 GUI 线程。
我不明白 SIGABRT 的原因,以及为什么我开始使用 boost::thread 后 host_ 和 port_ 的字段值会变得无效。
PS:同样的设置,在类似的命令行应用程序(没有 Qt GUI 代码)中的 boost::thread 行为正确,即当 networkService 类没有被黑客入侵以使 Qt 信号/插槽通知主 GUI 线程时。在这里,我使用 boost::asio 在 boost::thread 中的响应。
编辑: 根据对我的问题的回答,我尝试了这个......我禁用了网络服务类的 Q_OBJECT 信号/插槽和 QObject 派生,以确保 MOC 不会把事情搞砸.. 但问题仍然存在,我在 Windows 上遇到访问冲突与 Linux 上的 sigabrt。网络服务对象的字段被破坏的问题也存在,最终出现访问冲突。 实际上行为没有改变。
在启动线程之前: 从内螺纹 访问冲突继续...
所以,即使没有 MOC ,问题仍然存在。
编辑 2: 我很抱歉打扰..我犯了一个巨大的错误,即在 boost::thread 中使用本地 networkService 对象,当线程实际运行时,该对象被限定了!
【问题讨论】:
我没有机会检查(为这个低价值的评论道歉),但它可能与 qt 中的预编译步骤有关吗? Boost 有很多复杂的头文件,而且 qt 在调用 gcc 之前对代码做了一些奇怪的事情,这可能是一个让事情变得混乱的机会。总的来说,我一直认为混合应用程序范式是一种糟糕的形式。 boost asio 是前摄器,qt 是信号槽。事情可能会有点混乱! 【参考方案1】:问题在于,qt 中只允许一个线程操作 qt 中的 gui。那就是调用 QApplication::exec 的那个。这样做是为了消除 qt 用户的复杂性,并且由于 QApplication / 消息循环是单例的。话虽如此,在 qt 中,线程正在发生一些神奇的事情。所有的 QObjects 都被分配了一个线程。默认情况下,创建它们的那个。当建立信号槽连接时,将确定如何实际分派呼叫。如果对象属于同一线程,则通过直接/同步调用插槽来调度信号。如果将对象分配给可区分的线程,则消息从一个线程发送到另一个线程,以调用分配给槽所在对象的线程上的槽。这就是你在这里真正需要的。
您的代码的问题是您的两个 QObject 都是在同一个线程上创建的。因此,它们被分配了相同的线程。因此,操作 GUI 的插槽直接从您的工作线程调用,记住这是被禁止的!由于您的工作人员不是调用 QApplication::exec 的人。要在调用插槽时覆盖自动并说服正确执行线程切换,您必须在执行连接时使用 Qt::QueuedConnection。
connect(&ntwkSer, SIGNAL(responseReady(std::string)), this, SLOT(showResponse(std::string)), Qt::QueuedConnection);
【讨论】:
【参考方案2】:很难让 asio
io_service.run()
函数与 Qt 事件循环“玩得很好”。
使用调用io_service::poll()
或io_service::poll_one()
的Qt
插槽然后将该插槽连接到QTimerEvent
会更容易。
使用QNetworkAccessManager
而不是asio
更容易,请参阅Qt Client Example
【讨论】:
以上是关于使用 Qt 提升 asio的主要内容,如果未能解决你的问题,请参考以下文章