俄语在线网站建设/全国十大婚恋网站排名

俄语在线网站建设,全国十大婚恋网站排名,软件开发公司的组织架构,如何对网站进行分析在开发音视频播放器时,多线程设计是不可避免的挑战。音频和视频的解码、播放需要高效运行,同时还要与主线程或其他线程同步,例如通过信号通知播放进度。本文基于一个实际案例,分析了两种线程设计在死循环和信号槽使用中的表现&…

在开发音视频播放器时,多线程设计是不可避免的挑战。音频和视频的解码、播放需要高效运行,同时还要与主线程或其他线程同步,例如通过信号通知播放进度。本文基于一个实际案例,分析了两种线程设计在死循环和信号槽使用中的表现,探讨其原因,并给出选择建议。

问题表现

我在实现音频播放线程时,遇到了一个问题:主线程通过 QMetaObject::invokeMethod 调用 terminateDecode 无法终止音频线程,而直接调用 m_audioThread->terminateDecode() 却能稳定生效。后来,我将视频解码线程改为 QObject + QThread + std::thread 的设计后,invokeMethod 开始生效。这让我疑惑:为什么死循环会影响信号槽?如何选择合适的设计?

具体表现如下:

  1. 音频线程(AudioPlayerThread)
    • 使用 QThread 子类,重写 run() 为死循环。
    • 主线程调用 QMetaObject::invokeMethod(m_audioThread, "terminateDecode", Qt::QueuedConnection) 无效。
    • 直接调用 m_audioThread->terminateDecode() 生效。
  2. 视频线程(m_videoWorker)
    • 使用 QObject 移入 QThread,解码死循环在 std::thread 中。
    • QMetaObject::invokeMethod(m_videoWorker, "terminateDecode", Qt::QueuedConnection) 生效。

此外,音频线程需要发送 avClockUpdated 信号与视频同步,这在死循环中似乎不受影响。问题出在哪里?

原因分析

问题的根源在于 Qt 的信号槽机制与线程设计的关系,特别是事件循环的作用。

1. 信号发送(emit)不受死循环影响

  • 机制:在 Qt 中,emit 一个信号会根据连接类型(Qt::DirectConnection 或 Qt::QueuedConnection)直接调用槽函数或将信号放入目标线程的事件队列。emit 本身是线程安全的,不依赖事件循环。
  • 音频线程的表现
    emit avClockUpdated(current_pts);

  • 在 AudioPlayerThread 的死循环中,只要线程执行到 emit,信号就会发出,通知视频线程或其他组件。死循环不会阻止信号发送。

2. 信号接收需要事件循环

  • 机制:当使用 Qt::QueuedConnection 调用槽函数(如 terminateDecode),Qt 会将调用请求放入目标线程的事件队列,等待事件循环处理。如果线程没有事件循环,队列中的信号无法被执行。
  • 音频线程的问题
    • run() 是一个 while (true) 死循环:

void AudioPlayerThread::run() {while (true) {// 等待文件和播放逻辑}
}

 

    • 没有调用 exec(),事件循环未启动。
    • QMetaObject::invokeMethod 的调用被排队但无法处理,因此无效。
  • 直接调用的原因
    • m_audioThread->terminateDecode() 是同步调用,不依赖事件循环,直接修改 m_stopRequested 并唤醒条件变量,线程得以退出。

3. 视频线程的改进

  • 设计

    • m_videoWorker 是 QObject,通过 moveToThread 移入 QThread。
    • QThread 默认运行事件循环(exec())。
    • 解码死循环在 std::thread 中:
      class VideoWorker : public QObject {
      public:VideoWorker() {m_decodeThread = std::thread([this] { decodeLoop(); });}
      public slots:void terminateDecode() { m_stopRequested = true; }
      private:void decodeLoop() { while (!m_stopRequested) { /* 解码 */ } }std::thread m_decodeThread;std::atomic<bool> m_stopRequested{false};
      };



       

    • 结果
      • QThread 的事件循环处理 invokeMethod,触发 terminateDecode。
      • 死循环在 std::thread 中,不干扰事件循环。
    • 4. 音视频同步的需求

    • 音频线程发出 avClockUpdated 信号,视频线程接收以同步。
    • 如果视频需要控制音频(例如暂停),音频线程也需要接收信号。死循环设计在这方面受限。
    • 设计选择

      基于以上分析,我对比了两种设计:

      设计 1:AudioPlayerThread(QThread 死循环)

    • 特点
      • 重写 run() 为死循环,使用条件变量同步。
      • 通过直接调用控制线程。
    • 优点
      • 高效:无事件循环开销,适合音频实时性。
      • 简单:逻辑集中,易于实现。
    • 缺点
      • 无法接收信号:需手动检查状态。
      • 扩展性差:复杂控制需额外同步。
    • 适用场景:单一任务,实时性要求高。
    • 设计 2:m_videoWorker(QObject + QThread + std::thread)

    • 特点
      • QThread 运行事件循环,std::thread 执行死循环。
      • 通过信号槽控制。
    • 如何选择?

    • 音频实时性优先:选择设计 1,优化终止逻辑(例如在内层检查 m_stopRequested)。
    • 音视频同步和控制:选择设计 2,支持双向信号通信。
    • 我的需求:音频需要发送 avClockUpdated 与视频同步,可能还需要接收控制信号。设计 2 更合适。
    • 优化建议

      对于音视频同步,我推荐设计 2:

    • 优点
      • 支持信号槽:发送和接收信号都方便。
      • 扩展性强:适合音视频同步和复杂交互。
    • 缺点
      • 复杂性增加:多线程管理。
      • 轻微开销:事件循环和线程切换。
    • 适用场景:需要双向通信或 UI 交互。

 

// 音频工作类
class AudioWorker : public QObject {Q_OBJECT
public:AudioWorker() { m_audioThread = std::thread([this] { audioLoop(); }); }~AudioWorker() { m_stopRequested = true; if (m_audioThread.joinable()) m_audioThread.join(); }
public slots:void terminateDecode() { m_stopRequested = true; }
signals:void avClockUpdated(qint64 pts); // 发送音频时间戳
private:void audioLoop() {while (!m_stopRequested) {qint64 pts = /* 计算音频时间戳,例如 av_rescale_q */;emit avClockUpdated(pts); // 通知视频线程// 音频解码和播放逻辑std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟播放}}std::thread m_audioThread;std::atomic<bool> m_stopRequested{false};
};// 视频工作类
class VideoWorker : public QObject {Q_OBJECT
public:VideoWorker() { m_videoThread = std::thread([this] { videoLoop(); }); }~VideoWorker() { m_stopRequested = true; if (m_videoThread.joinable()) m_videoThread.join(); }
public slots:void terminateDecode() { m_stopRequested = true; }void syncWithAudio(qint64 audioPts) { m_currentAudioPts = audioPts; // 根据音频时间戳调整视频播放}
private:void videoLoop() {while (!m_stopRequested) {qint64 videoPts = /* 计算视频时间戳 */;if (m_currentAudioPts > 0 && std::abs(videoPts - m_currentAudioPts) > 100) {// 如果视频与音频时间差过大,调整播放(例如跳帧或等待)}// 视频解码和渲染逻辑std::this_thread::sleep_for(std::chrono::milliseconds(40)); // 模拟播放}}std::thread m_videoThread;std::atomic<bool> m_stopRequested{false};std::atomic<qint64> m_currentAudioPts{0};
};// 主线程中的管理类
class MainClass : public QObject {Q_OBJECT
public:void startAV() {// 初始化音频线程m_audioThread = new QThread();m_audioWorker = new AudioWorker();m_audioWorker->moveToThread(m_audioThread);// 初始化视频线程m_videoThread = new QThread();m_videoWorker = new VideoWorker();m_videoWorker->moveToThread(m_videoThread);// 连接音频信号到视频槽,实现同步connect(m_audioWorker, &AudioWorker::avClockUpdated, m_videoWorker, &VideoWorker::syncWithAudio, Qt::QueuedConnection);// 启动线程m_audioThread->start();m_videoThread->start();}void stopAV() {if (m_audioThread) {QMetaObject::invokeMethod(m_audioWorker, "terminateDecode", Qt::QueuedConnection);m_audioThread->quit();m_audioThread->wait();delete m_audioThread;delete m_audioWorker;}if (m_videoThread) {QMetaObject::invokeMethod(m_videoWorker, "terminateDecode", Qt::QueuedConnection);m_videoThread->quit();m_videoThread->wait();delete m_videoThread;delete m_videoWorker;}}private:QThread* m_audioThread = nullptr;AudioWorker* m_audioWorker = nullptr;QThread* m_videoThread = nullptr;VideoWorker* m_videoWorker = nullptr;
};

 

总结

  • 信号发送:死循环不影响 emit,音频可以通知视频。
  • 信号接收:死循环无事件循环,需用设计 2 或状态检查解决。
  • 选择依据:实时性选设计 1,同步和扩展性选设计 2。

通过将死循环移到 std::thread,结合 QThread 的事件循环,我实现了音频和视频的高效同步。这种设计既满足了实时性,又提供了灵活性,是音视频播放器的推荐方案。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/73968.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

knowledge-微前端(多个前端应用聚合的一个应用架构体系,每个小的应用可独立运行,独立开发,独立部署上线)

1.前言 微前端&#xff0c;将一个大的前端应用拆分为多个小型的&#xff0c;独立开发的前端应用&#xff0c;每一个小型的应用都可以单独的开发&#xff0c;部署和运行。这种结构允许不同的团队使用不同的技术栈来开发应用的不同部分&#xff0c;提高开发的效率与灵活性。 2.实…

工厂函数详解:概念、目的与作用

一、什么是工厂函数&#xff1f; 工厂函数&#xff08;Factory Function&#xff09;是一种设计模式&#xff0c;其核心是通过一个函数来 创建并返回对象&#xff0c;而不是直接使用 new 或构造函数实例化对象。它封装了对象的创建过程&#xff0c;使代码更灵活、可维护。 二、…

C语言-指针变量和变量指针

指针 预备知识 内存地址 字节&#xff1a;字节是内存的容量单位&#xff0c;英文名Byte&#xff0c;1Byte8bits 地址&#xff1a;系统为了便于区分每一个字节面对它们的逐一进行编号&#xff08;编号是唯一的&#xff09;&#xff0c;称为内存地址&#xff0c;简称地址。int…

unityAB包(1/2)

unityAB包学习 1.AB包的导出扩展BuildAssetBundleOptions无特殊选项压缩相关选项 2.AB包资源管理3.Resource和AssetBundle加载方式的区别4.预设体5.Unity Asset Bundle Browser 工具5为什么要勾选拷贝到StreamingAsset里面。6.AB包的加载 1.AB包的导出 首先在Project窗口&…

某酒企数字化转型及电商规划项目启动会暨培训会v(60页PPT)(文末有下载方式)

详细资料请看本解读文章的最后内容。 在当今数字化浪潮席卷之下&#xff0c;企业的发展面临着前所未有的机遇与挑战。对于某酒企而言&#xff0c;数字化转型和电商规划已成为其实现 “二次腾飞”、迈向世界级酒企的关键战略举措。本次启动会暨培训会&#xff0c;为该酒企的转型…

NET6 WebApi第5讲:中间件(源码理解,俄罗斯套娃怎么来的?);Web 服务器 (Nginx / IIS / Kestrel)、WSL、SSL/TSL

一、NET6的启动流程 区别&#xff1a; .NET6 WebApi第1讲&#xff1a;VSCode开发.NET项目、区别.NET5框架【两个框架启动流程详解】_vscode webapi-CSDN博客 2、WebApplicationBuilder&#xff1a;是NET6引入的一个类&#xff0c;是建造者模式的典型应用 1>建造者模式的…

【AI】AI编程助手:Cursor、Codeium、GitHub Copilot、Roo Cline、Tabnine

文章目录 一、基本特性对比二、收费标准三、私有部署能力1、Tabnine2、Roo Code 三、代码补全与自然语言生成代码四、安装独立的IDE安装插件安装 五、基本使用&#xff08;一&#xff09;Cursor&#xff08;二&#xff09;GitHub Copilot1、获取代码建议2.聊天1&#xff09;上下…

三轴云台之角速度信号篇

三轴云台的角速度信号主要通过其内置的传感器&#xff08;如陀螺仪&#xff09;来感知和测量。 一、角速度信号的感知与测量 在三轴云台中&#xff0c;陀螺仪是测量角速度的关键组件。它通常安装在三个互相垂直的轴上&#xff08;通常为X、Y、Z轴&#xff09;&#xff0c;能够…

绿盟春招实习一面

《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

进制转换(R转十)(1290. 二进制转换十进制、1292. 十六进制转十进制、1291. 八进制转十进制、1405. 小丽找潜在的素数)

题单地址&#xff1a;题单中心-东方博宜OJ 这里以二进制转十进制为例&#xff08;按位加权求和法&#xff09; 1290. 二进制转换十进制 问题描述 请将一个 25 位以内的 2 进制正整数转换为 1010 进制&#xff01; 输入 一个 25 位以内的二进制正整数。 输出 该数对应的…

9.嗅探与Wireshark进阶分析

嗅探与Wireshark进阶分析 第一部分&#xff1a;嗅探的概念与重要性第二部分&#xff1a;Wireshark进阶功能第三部分&#xff1a;嗅探实践与分析总结 目标&#xff1a; • 理解嗅探&#xff08;Sniffing&#xff09;的概念及其在网络安全中的作用 • 掌握Wireshark的进阶功能&a…

【Linux篇】进程控制

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 1. 进程创建 1.1 fork函数 在linux中fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个…

HyperAD:学习弱监督音视频暴力检测在双曲空间中的方法

文章目录 速览摘要1. 引言2. 相关工作弱监督暴力检测双曲空间中的神经网络 3. 预备知识双曲几何切空间&#xff08;Tangent Space&#xff09;指数映射与对数映射&#xff08;Exponential and Logarithmic Maps&#xff09;3.1 双曲图卷积网络&#xff08;Hyperbolic Graph Con…

动态规划(6.不同路径II)

题目链接&#xff1a;63. 不同路径 II - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; 本题为不同路径的变型&#xff0c;只不过有些地方有「障碍物」&#xff0c;只要在「状态转移」上稍加修改就可解决。 状态表示&#xff1a; 对于这种Γ路径类」的问题&#xf…

深度洞察:DeepSeek 驱动金融行业智能化转型变革

该文章为软件测评&#xff0c;不是广告&#xff01;&#xff01;&#xff01;&#xff01; 目录 一.金融行业的智能化转型浪潮​ 二.DeepSeek的核心技术剖析 1.DeepSeek 模型的金融智慧​ 2.实时联网搜索&#xff1a;把握金融市场脉搏​ 3.RAG 能力&#xff1a;铸就精准金…

蓝桥杯备考----》暴力枚举---金盏花

这道题&#xff0c;一共12位&#xff0c;给了后六位&#xff0c;我们只要枚举前六位就行了&#xff0c;当然如果是10的12次方的话&#xff0c;必须要开long long才可以存下&#xff0c;这点我们不要忘了 然后题目中又告诉了没有前导0&#xff0c;我们可以从100000开始枚举&…

RAG各类方法python源码解读与实践:利用Jupyter对RAG技术综合评测【3万字长文】

检索增强生成&#xff08;RAG &#xff09;是一种结合信息检索与生成模型的混合方法。它通过引入外部知识来提升语言模型的性能&#xff0c;从而提高回答的准确性和事实正确性。为了简单易学&#xff0c;不使用LangChain框架或FAISS向量数据库&#xff0c;而是利用Jupyter Note…

Python列表2

print("—————————— 列表的相关操作 ————————————")lst.append(x)在列表lst最后增加一个元素 lst.insert(index,x)在列表中第index位置增加一个元素 lst.clear()清除列表lst中所有元素 lst.pop(index)将列表lst中第index位置的元素取出&#xf…

C语言复习笔记--数组

今天继续来浅浅推进一下C语言的复习,这次是数组的复习,话不多说,正文开始. 数组的概念 数组是⼀组相同类型元素的集合,一种自定义类型.数组中元素个数不能为0.数组分为⼀维数组和多维数组&#xff0c;多维数组⼀般⽐较多⻅的是⼆维数组. 下面从一维数组说起. 一维数组的创建和…

Canal 解析与 Spring Boot 整合实战

一、Canal 简介 1.1 Canal 是什么&#xff1f; Canal 是阿里巴巴开源的一款基于 MySQL 数据库增量日志解析&#xff08;Binlog&#xff09;中间件&#xff0c;它模拟 MySQL 的从机&#xff08;Slave&#xff09;行为&#xff0c;监听 MySQL 主机的二进制日志&#xff08;Binl…