You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QT GUI按钮启停USRP录制:如何彻底终止含UHD子线程的任务

如何彻底终止UHD USRP录制线程及子线程?

问题分析

你当前的实现使用QThread::terminate()强制终止线程,但这种方式存在两个核心问题:

  1. 不安全且无效:terminate()仅能终止Qt创建的线程,无法影响UHD库内部自行创建的子线程,导致录制流程无法彻底停止。
  2. 无协作退出逻辑:run_usrp是阻塞式执行的函数,没有主动检查停止信号的逻辑,即使主线程发出终止命令,它也不会主动退出。

要实现类似CTRL-C的彻底终止效果,必须采用协作式停止+UHD原生终止接口的组合方案,而非强制终止线程。

解决方案

步骤1:添加线程安全的停止标志

在MainWindow类中添加原子布尔变量,用于跨线程传递停止信号(原子变量保证多线程访问的安全性):

// MainWindow.h
#include <atomic>

class MainWindow : public QMainWindow
{
    Q_OBJECT
    // ... 其他已有成员
private:
    std::atomic<bool> m_stopRequested{false};
    QThread* m_usrpThread = nullptr; // 保存线程指针,方便后续管理
};

步骤2:重构EXECUTE按钮逻辑

移除QThread::terminate()的连接,改为保存线程指针并绑定线程结束后的清理逻辑:

void MainWindow::on_button_EXECUTE_clicked()
{
    if (ui->calculated_StartTime->text().isEmpty())
    {
        QMessageBox::critical(this, "Error", "Hit the \"CALCULATE SCHEDULE\" button first above!!");
        return;
    }

    ui->button_STOP->setVisible(true);
    ui->button_EXECUTE->setVisible(false);

    m_stopRequested.store(false); // 重置停止标志
    m_usrpThread = QThread::create([this]{ execute_run_usrp(); });
    
    // 线程结束后自动清理并恢复UI状态
    connect(m_usrpThread, &QThread::finished, [this]{
        m_usrpThread->deleteLater();
        m_usrpThread = nullptr;
        ui->button_STOP->setVisible(false);
        ui->button_EXECUTE->setVisible(true);
    });

    m_usrpThread->start();
}

步骤3:修改execute_run_usrp传递停止标志

将停止标志传递给run_usrp函数,让它能感知停止请求:

void MainWindow::execute_run_usrp()
{
    float startFreq = ui->input_startFreq->text().toFloat();
    float stopFreq = ui->input_stopFreq->text().toFloat();
    float stepFreq = ui->input_stepFreq->text().toFloat();
    int nRepeats = ui->input_numRepeats->text().toInt();
    float ipp = ui->input_IPP->text().toFloat();
    int sweepCadence = ui->calculated_sweepCadence->text().toInt();
    int numSweeps = ui->input_numSweeps->text().toInt();
    std::string schedule_run = ui->calculated_StartTime->text().toStdString();

    // 传递停止标志引用给run_usrp
    run_usrp(startFreq, stopFreq, stepFreq, nRepeats, ipp, sweepCadence, numSweeps, schedule_run, m_stopRequested);
}

步骤4:修改run_usrp添加停止检查与UHD终止逻辑

在run_usrp中定期检查停止标志,并调用UHD原生接口终止录制流程:

// 修改run_usrp的函数签名,新增停止参数
void run_usrp(float startFreq, float stopFreq, float stepFreq, int nRepeats, float ipp, int sweepCadence, int numSweeps, const std::string& schedule_run, std::atomic<bool>& stopRequested)
{
    // 初始化UHD设备
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make("");
    // ... 配置频率、采样率等参数

    // 创建接收流对象
    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(...);

    // 启动连续流(示例)
    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
    rx_stream->issue_stream_cmd(stream_cmd);

    // 录制循环逻辑
    std::vector<std::complex<float>> buff(1024);
    uhd::rx_metadata_t md;
    while (numSweeps-- > 0)
    {
        // 每次循环前检查停止标志
        if (stopRequested.load(std::memory_order_acquire))
        {
            // 发送停止流命令给UHD
            rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
            break;
        }

        // 带超时的接收操作,避免永久阻塞
        size_t num_rx = rx_stream->recv(&buff[0], buff.size(), md, 0.1); // 0.1秒超时
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT || stopRequested.load())
        {
            rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
            break;
        }

        // ... 处理接收的数据
    }

    // 销毁UHD设备对象,自动终止所有内部子线程并释放资源
    usrp.reset();
}

步骤5:修改STOP按钮逻辑触发停止请求

void MainWindow::on_button_STOP_clicked()
{
    m_stopRequested.store(true, std::memory_order_release);
    
    // 可选:请求线程中断,配合QThread::isInterruptionRequested()使用
    if (m_usrpThread)
    {
        m_usrpThread->requestInterruption();
    }
}

方案说明

  1. 协作式停止:通过原子变量传递停止信号,让run_usrp主动检查并退出,避免强制终止线程带来的资源泄漏问题。
  2. UHD原生终止:调用issue_stream_cmd发送停止命令,直接通知UHD的流线程停止工作;销毁usrp对象会彻底释放所有UHD创建的子线程和硬件资源,达到与CTRL-C一致的终止效果。

内容的提问来源于stack exchange,提问作者NoRainDropsInTheSky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:45:37