QT 多线程中使用QCanBusDevice进行PCAN通讯时,无法正常发出数据

2023-11-17

QT 多线程中使用QCanBusDevice进行PCAN通讯时,无法正常发出数据

前言

我一开始的代码逻辑是,PCAN开启、关闭、发送、接收这些功能整合在一个工具类中,这个工具类的对象是在主线程创建的,然后我有一个要循环定时发送的功能是独立的一个线程,我在这个线程调用发送函数时,虽然返回的都是true,但是抓报文后却发现没有发出来。

出错代码结构(模拟)

/* 打开PCAN函数 */
void MainWindow::on_btn_open_clicked()
{
	...
	m_canDevice=QCanBus::instance()->createDevice(...);
	/* PCAN启动成功执行 */
	if (m_canDevice)
	{
	  	...
		canOpened = true; /* PCAN开启标志位 */
		/* 创建定时循环发送线程 */
		QFuture<void> thread = QtConcurrent::run(this, thread_send_data); 
	}
}
/* 定时循环发送函数 */
void MainWindow::thread_send_data()
{
    while (canOpened)
    {
        send_data();			/* 直接调用发送函数 */
        QThread::msleep(1000);  /* 定时1秒发送一次 */
    }
}
/* 发送函数,由于是Demo,所以是写死的 */
void MainWindow::send_data()
{
    QCanBusFrame frame;
    QByteArray data;
    data.resize(8);
    data[0]= 0x70;
    data[1]= 0xFF;
    data[2]= 0xFF;
    data[3]= 0xFF;
    data[4]= 0xFF;
    data[5]= 0xFF;
    data[6]= 0xFF;
    data[7]= 0xFF;
    quint32 ID = 0x18510004;
    frame.setFrameType(QCanBusFrame::DataFrame);
    frame.setExtendedFrameFormat(ID > 0x7FF ? true : false); /* 是否为扩展帧 */
    frame.setTimeStamp(QDateTime::currentMSecsSinceEpoch());
    frame.setFrameId(ID);
    frame.setPayload(data);
    /* m_canDevice是主线程中创建的QCanBusDevice对象 */
    if (m_canDevice->writeFrame(frame))
    {
        qDebug()<<"发送成功";
        return;
    }
    qDebug()<<"发送失败";
}

原因

在QT编辑器的控制台中,出现了以下错误提示:

QObject::startTimer: Timers cannot be started from another thread

可以明显看出,它说定时器不能在其他线程开启,说明发送函数的底层用到了定时器,且需要将发送函数在对象的同一线程中执行。
我之前是直接调用的,因为QCanBusDevice对象是在主线程中创建的,所以在其他线程中用这个对象的发送函数就出错了,这里就可以使用信号与槽机制,把发送这个步骤切换到主线程中执行。

解决方法

创建一个信号:

void sendData();

连接信号与槽,把发送信号和发送函数连接起来:

connect(this, &MainWindow::sendData, this, &MainWindow::send_data);

修改定时循环函数:

void MainWindow::thread_send_data()
{
    while (canOpened)
    {
        emit sendData();		/* 直接调用改为发送信号 */
        QThread::msleep(1000);
    }
}

深究

那么为什么使用了信号与槽后,问题就解决了呢?
我们可以认定,使用了信号与槽后,发送函数是在主线程中执行的,其实connect函数还有第五个参数,这个参数可以设置槽函数同步还是异步,甚至在哪一方执行。
在这里插入图片描述
Qt::ConnectionType
参考博客:https://blog.csdn.net/weixin_41761608/article/details/119237172

类型 功能
Qt::AutoConnection 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
Qt::DirectConnection 槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
Qt::QueuedConnection 槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
Qt::BlockingQueuedConnection 槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
Qt::UniqueConnection 这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

如果我们要知道是否发送成功,就可以把发送函数和发送信号的类型改为bool,发送成功返回true,发送失败返回false,而连接的类型应使用Qt::BlockingQueuedConnection,如果用默认或者Qt::QueuedConnection都无法获取到发送的状态,,一直为false,因为不是同步的。

最终代码

signals:
	bool sendData();
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(this, &MainWindow::sendData, this, &MainWindow::send_data, Qt::BlockingQueuedConnection);
}
/* 打开PCAN函数 */
void MainWindow::on_btn_open_clicked()
{
	...
	m_canDevice=QCanBus::instance()->createDevice(...);
	/* PCAN启动成功执行 */
	if (m_canDevice)
	{
	  	...
		canOpened = true; /* PCAN开启标志位 */
		/* 创建定时循环发送线程 */
		QFuture<void> thread = QtConcurrent::run(this, thread_send_data); 
	}
}
/* 定时循环发送函数 */
void MainWindow::thread_send_data()
{
    while (canOpened)
    {
    	/* 发送失败执行 */
        if (!sendData())
        {
			...
		}
        QThread::msleep(1000);  /* 定时1秒发送一次 */
    }
}
/* 发送函数,由于是Demo,所以是写死的 */
bool MainWindow::send_data()
{
    QCanBusFrame frame;
    QByteArray data;
    data.resize(8);
    data[0]= 0x70;
    data[1]= 0xFF;
    data[2]= 0xFF;
    data[3]= 0xFF;
    data[4]= 0xFF;
    data[5]= 0xFF;
    data[6]= 0xFF;
    data[7]= 0xFF;
    quint32 ID = 0x18510004;
    frame.setFrameType(QCanBusFrame::DataFrame);
    frame.setExtendedFrameFormat(ID > 0x7FF ? true : false); /* 是否为扩展帧 */
    frame.setTimeStamp(QDateTime::currentMSecsSinceEpoch());
    frame.setFrameId(ID);
    frame.setPayload(data);
    /* m_canDevice是主线程中创建的QCanBusDevice对象 */
    return m_canDevice->writeFrame(frame);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT 多线程中使用QCanBusDevice进行PCAN通讯时,无法正常发出数据 的相关文章

  • 如何使用bind将成员函数作为函数指针传递?

    我试图将成员函数作为函数指针传递 这样我就不需要依赖单例或全局函数来处理 Qt 5 中的 Qt 消息 据我所知 我的 std function 是正确的类型 它具有正确的签名 并且绑定应该允许我插入隐式this指针 本质上是将成员函数传递为
  • QGraphicsSimpleTextItem“无效使用不完整类型”

    我的代码如下 指针部件 h QGraphicsSimpleTextItem text 指针控件 cpp void PointerWidget placeNumbers float spacing int currentTickNumber
  • 在另一个中使用 QAbstractListModel

    我在尝试使用 Qt QML 为我的应用程序开发数据模型时遇到问题 我已经用过一个QAbstractListModel能够将海关数据模型从 C 传递到 QML 并且它对于简单模型 例如基于字符串和布尔的模型 来说就像一个魅力 但现在我需要建立
  • qvariant 作为 qhash 中的键

    我想创建一个带有 QVariants 键的数据结构 它看起来像这样 QHash
  • 构建qt程序时未定义的符号:找不到qt_version_tag

    我正在学习Qt5 6 我正在使用 Ubuntu 14 4 当我链接我的程序时 出现以下错误 undefined reference to qt version tag 在 CMakeLists txt 中 link libraries Qt
  • PyQt:如何通过匿名代理使用网页

    这真让我抓狂 我想在 QWebPage 中显示一个 url 但我想通过匿名代理来实现 Code setting up the proxy proxy QNetworkProxy proxy setHostName 189 75 98 199
  • 我如何告诉 QTableWidget 结束编辑单元格?

    我正在显示一个弹出菜单来选择 QTableWidget 中的某些值 最下面的项目是 修改列表 条目 当我选择它时 应自动出现一个新窗口 并且 QComboBox 应消失 并且单元格返回到 Qt DisplayRole 状态 现在 Qt 拥有
  • 更改 Qt 中的语言环境

    我尝试使用 QLocale 和 setDefault 函数更改区域设置 但似乎不起作用 以下是使用 C 本地化库和 QLocale 更改语言环境的示例 对于 C 本地化库 它似乎可以工作 但对于 QLocale setDefault 函数调
  • 仅当从 Qt 连接时网页返回 HTTP 406 错误

    我有一个测试页面设置http mlecturedownload com test qt php http mlecturedownload com test qt php有以下代码
  • 在 Windows 上静默安装 Qt55 Enterprise

    编辑 在 Qt 支持的帮助下 我已经解决了如何自动化 Qt 企业安装程序的这两个部分 下面是脚本调用 我正在尝试在 Windows 8 1 和 Windows 10 上静默安装 Qt 5 5 1 Enterprise 使用 script 开
  • QGraphicsView 在完整布局中未最大化

    I have following GUI having four QGraphicView objects 正如您在每个视图下看到的那样 它有四个工具按钮 为了最大化视图 我连接了工具按钮的信号来隐藏其他三个视图的插槽 并将大小策略设置为扩
  • 如何阻止 Qt Creator 将可执行文件放置在“调试”子目录中?

    我正在 Qt Creator 中构建一个项目 虽然我不关心中间 obj 文件去哪里 但重要的是最终的可执行文件应放入 并运行 许多依赖项 DLL 等所在的特定目录中 被发现 因此 在 Qt Creator 中 我选择 Shadow Buil
  • Qt ObjectName() 必须是唯一的吗?

    如标题所示的简单问题 如果我打电话setObjectName 在一个对象上 它是否必须是唯一的 或者只是因为约定而推荐 我已经子类化了QLabel 并希望自动为创建的对象命名 如果这是一个坏主意 我会找到一些设置随机唯一名称的方法 我实际上
  • 将 QMAKE_CXXFLAGS += -std=c++11 添加到 qt 中的 .pro 文件不起作用(在 linux 12.04 上)

    编译器输出 main o 错误 1 cc1plus 错误 无法识别的命令行选项 std c 11 解决方案是更新系统上的旧 gcc 版本 事实上 我很惊讶你的 12 04 Ubuntu 变体上有这么旧的版本 默认情况下应该有 4 6 3 您
  • QTableView 选择已更改

    我有一个QTableView我需要从中获取选择更改事件 我似乎无法让连接工作 我有 MyWidget h protected slots void slotLoadTransaction const QItemSelection selec
  • 在 4K 屏幕上使用 Matplotlib 和 TKAgg 或 Qt5Agg 后端

    我在 Ubuntu 16 04 上使用 Matplotlib 2 0 和 Python 3 6 来创建数据图 电脑显示器的分辨率为 4k 分辨率为 3840x2160 绘图数字看起来非常小 字体也很小 我已经尝试过TKAgg and Qt5
  • QGraphicsScene没有删除QWidget的功能

    QGraphicsScene 有一个addWidget QWidget 有函数 但是没有对应的removeWidget QWidget 它只有removeItem QGraphicsItem 如何删除 QWidget 这是一个基本示例 看看
  • 第一个随机数始终小于其余随机数

    我碰巧注意到 在 C 中 使用 std rand 方法调用的第一个随机数大多数时候都明显小于第二个随机数 关于 Qt 实现 第一个几乎总是小几个数量级 qsrand QTime currentTime msec qDebug lt lt q
  • 获取 QPushButton 在 2D 数组 QPushButton 上的索引

    我有一个二维数组QPushButton 当用户单击按钮时如何获取按钮的索引 例如当用户单击按钮时a 2 3 它会显示 2 3 该示例如下所示 Qt 4 5 使用对象名称 您可以为按钮指定唯一的对象名称 理想情况下 名称应该是有效的 C 标识
  • 我应该使用 QCoreApplication::processEvents() 还是 QApplication::processEvents()?

    我有一个从两者调用的方法QThreads和主线程 这个方法有时可能需要很长时间才能在循环中进行计算 所以我把QCoreApplication processEvents 这可以防止 GUI 冻结 在某个时刻我已经改变了QCoreApplic

随机推荐

  • video标签在安卓下微信x5浏览器的一些问题

    强制全屏播放视频 非腾讯视频 Click事件不能触发 play 方法 只能通过video标签自己的播放按钮进行播放 因为播放只能通过video标签自己的播放按钮 所以readyState和canplaythrough视频就绪状态也没用 不能
  • nginx 请求头转发

    前言 网站安全 开始审查了 之前我们的项目 是通过 ip port 直接访问tomcat的 内部使用嘛 然而 现在需要使用 https 协议访问 以增强网站的安全性 本来没有这么麻烦的 但是写代码的人 竟然这样写的资源请求 现在访问模式 是
  • django中配置xadmin时报错metaclass conflict: the metaclass of a derived class must be a (non-strict) subcla

    报错信息如下 原因 没有修改我们定义的ModelAdmin的继承对象为object或去掉继承 错误代码如下 xadmin sites register Comment class CommentAdmin admin ModelAdmin
  • 华为OD机试真题-字符串比较-2023年OD统一考试(B卷)

    题目描述 给定字符串A B和正整数V A的长度与B的长度相等 请计算A中满足如下条件的最大连续子串的长度 1 该连续子串在A和B中的位置和长度均相同 2 该连续子串 A i B i 之和小于等于V 其中 A i B i 表示两个字母ASCI
  • 【转】密封类

    10 3 1 密封类概述及声明 10 3 密封类与密封方法 如果所有的类都可以被继承 那么很容易导致继承的滥用 进而使类的层次结构体系变得十分复杂 这样使得开发人员对类的理解和使用变得十分困难 为了避免滥用继承 C 中提出了密封类的概念 本
  • XStream 实现 javabean 与 xml 互转

    XStream 是什么 XStream 是一个 java类库 实际上是一个转换器 XStream 作用 实现 java bean 与 xml 互转 XStream 如何使用 主要通过 xStream 实现 转换 具体测试示例代码如下 源码
  • 运维工具

    环境部署 部署软件 如Apache Nginx tomcat JDK PHP MySQL等等 还需要测试吧 那就还需要部署一套测试环境 有些时候 开发环境也是需要运维来部署的 排错和调优 运维的事 尽快定位问题 解决问题才是王道 定位问题
  • 谁说毕业即失业?爬虫就业月入13000+第一个不服!

    不知道是Python太火爆 还是年轻人太卷 最近一段时间 老有刚毕业的年轻人向我请教Python问题 问题还相对比较专业 其中不乏有 财务小年轻请教Python数据统计 新媒体的朋友问Python爬虫 当然更多的还是来咨询Python爬虫就
  • Java监听器(重点)

    监听器 Listener 是一个实现特定接口的 Java 程序 这个程序专门用于监听另一个 Java 对象的方法调用或属性改变 当被监听对象发生上述事件后 监听器某个方法将立即自动执行 监听器的相关概念 事件 方法调用 属性改变 状态改变等
  • windows 安装linux子系统详细步骤

    1 dism exe online enable feature featurename Microsoft Windows Subsystem Linux all norestart 2 dism exe online enable fe
  • 对课程学习的期待

    我希望通过学习这门课程之后 自己可以设计出一些简单的程序 有利于之后的学习 例如 网络爬虫和一些有关于数据 图像处理的程序 我希望在理论课上老师可以通过一些有趣的示例来讲解课程内容 我认为这样可以更好的抓住学生的注意力而且可以使我们更容易理
  • 【无需显卡】AI绘画入门教程

    前言 Hello 各位端午节快乐呀 不好意思拖更两个月 最近实在是太忙了 也想不到有什么好玩的 之前介绍过了几个好玩的ai网站 非常适合新手尝鲜 但很多都有额度限制 而且还开始收费了 所以有了这期教程 还没看过上期可以看一下 除了ChatG
  • socks协议详解

    0x01 socks协议简介 Socks Socket Secure 协议是一种网络协议 处于会话层 用于管理网络连接并提供安全性和隐私保护 通过使用Socks代理服务器 客户端可以隐藏其真实IP地址和其他身份信息 从而匿名地访问互联网上的
  • php命令行传递参数,PHP命令行传递参数

    在以非命令行模式运行PHP的时候 我们可以通过 GET POST REQUEST来接受get或是post参数 但是以命令行方式运行的话 怎么传递参数以接受参数呢 关于命令行的特点 可以直接看官方文档 http php net manual
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • python 读写pcd

    1 读点云的3种方式 第一种 pip3 install python pcl import pcl pcd ndarray pcl load args pcd path to array 3 不要intensity pcd ndarray
  • 浏览器打开就是360导航(浏览器被360劫持)

    浏览器打开就是360导航 这个问题之前只是看别人帖子见到过 不知道出了什么问题我的edge和Chrome浏览器突然打开也成了360的导航页面 这才感觉出这个问题的恶心之处 而且顺道说一下 我电脑中也没有装任何360系的应用 但突然就被改了
  • 黑客基础知识——SYN泛洪攻击原理及防御

    拒绝服务攻击时 攻击者想非法占用被攻击者的一些资源 比如如 带宽 CPU 内存等等 使得被攻击者无法响应正常用户的请求 讲泛洪攻击之前 我们先了解一下DoS攻击和DDoS攻击 这两个攻击大体相同 前者的意思是 拒绝服务攻击 后者的意思是 分
  • docker下mysql镜像初始化

    目录 1 介绍 2 部署及验证 2 1 场景复现 2 2 创建dockerfile 2 3 初始化脚本 2 4 构建镜像并查看 2 5 创建容器并验证 2 6 完成 1 介绍 原理 当Mysql容器首次启动时 会在 docker entry
  • QT 多线程中使用QCanBusDevice进行PCAN通讯时,无法正常发出数据

    QT 多线程中使用QCanBusDevice进行PCAN通讯时 无法正常发出数据 前言 我一开始的代码逻辑是 PCAN开启 关闭 发送 接收这些功能整合在一个工具类中 这个工具类的对象是在主线程创建的 然后我有一个要循环定时发送的功能是独立