QT多线程实战经验

让线程在堆上分配

        比如有一个blueToothWorker,继承了QThread,实现了run方法。

        用的时候如果直接

        blueToothWorker bw;

        那么该线程变量就是在函数栈上分配,一旦函数结束,线程没执行完,线程变量就被回收了。

        正确用法是堆上分配。并通过connect函数自动回收。

void BluetoothMonitor::getBluetoothDataFromConDev()
{
    blueToothWorker* bw = new blueToothWorker();
    QObject::connect(bw, &QThread::finished, bw, &QObject::deleteLater);
    bw->start();
}

成员指针变量的注意点

           下面这种指针变量没有初始话,导致可能指向任意地址,也就是野指针

        记得初始化的时候指向nullptr

private:
    QFileSystemWatcher *watcher;//文件监视器
    QString filePath;//文件路径
    blueToothWorker* bluetoothWorker; // 线程对象

        如果delete以后,指针原本指向的地址被回收,但指针还是指向那里,造成悬空指针

int *p = new int(10);
delete p; // p 成为悬空指针
*p = 20;  // 未定义行为

        记得delete以后指向nullptr

让线程安全停止

        QThread没有给stop方法来停止线程。

        QT所推荐的停止线程的方式,是自定义一个成员方法stop,在stop里面设置标志位,去控制run方法里面的循环。

void blueToothWorker::stop(){
    running = false;
}

        通过

 QObject::connect(bluetoothWorker, &QThread::finished, 
                bluetoothWorker, &QObject::deleteLater);

        来在线程任务完成后安全地回收线程。

        同时,如果需要,可以调用wait方法来等待线程停止。

volatile的作用

        前面提到了run里面跑while循环,现在,循环通过标志位volatile bool running来判断。

防止编译器优化

        假设有一个变量flag,用于表示某个操作是否完成。

        在没有volatile修饰的情况下,编译器可能会对代码进行优化,导致flag的值被缓存在寄存器中,而不是每次都从内存中读取。

int flag = 0;

void checkFlag() {
    while (flag == 0) {
        // 等待flag被设置为1
    }
    // 执行后续操作
}

        如果flag被其他线程或中断服务程序修改为1,但编译器优化后将flag的值缓存在寄存器中,那么checkFlag函数中的while循环可能会一直执行,因为寄存器中的值始终是0。

        但如果将flag声明为volatile: 

volatile int flag = 0;

        编译器就不会对其进行优化,每次访问flag时都会从内存中读取其值,从而能够及时检测到flag的变化。 

内存可见性

        在一个多线程程序中,线程A修改一个变量count,线程B需要读取count的最新值。

int count = 0;

// 线程A
void incrementCount() {
    count++;
}

// 线程B
void printCount() {
    std::cout << count << std::endl;
}

        如果没有volatile修饰,线程A对count的修改可能不会及时写回内存,导致线程B读取到的count值不是最新的。

        但如果将count声明为volatile,线程A对count的修改会及时写回内存,线程B能够读取到最新的值。

禁止指令重排序

        假设有一个变量data和一个标志变量ready,线程A先将data赋值,然后将ready设置为true,线程B在readytrue时读取data

int data = 0;
bool ready = false;

// 线程A
void setData() {
    data = 42;
    ready = true;
}

// 线程B
void readData() {
    if (ready) {
        std::cout << data << std::endl;
    }
}

        如果没有volatile修饰,编译器可能会对指令进行重排序,导致ready先被设置为true,而data的赋值操作后置,这会导致线程B在readytrue时读取到data的旧值。

volatile int data = 0;
volatile bool ready = false;

        编译器不会对访问这些变量的指令进行重排序,从而保证了程序的逻辑正确性。

volatile常用场景

        中断服务程序中修改的变量

        多任务环境下的共享变量

        存储器映射的硬件寄存器

volatile bool interruptFlag = false;

// 中断服务程序
void interruptHandler() {
    interruptFlag = true;
}

// 主程序
void mainProgram() {
    while (!interruptFlag) {
        // 等待中断发生
    }
    // 处理中断
}
volatile bool taskCompleted = false;

// 任务A
void taskA() {
    // 执行任务A的工作
    taskCompleted = true;
}

// 任务B
void taskB() {
    while (!taskCompleted) {
        // 等待任务A完成
    }
    // 执行任务B的工作
}
volatile int* reg = reinterpret_cast<volatile int*>(0x1000); // 假设寄存器地址为0x1000

void readReg() {
    int value = *reg; // 读取寄存器的值
    std::cout << value << std::endl;
}

void writeReg(int value) {
    *reg = value; // 写入寄存器
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大象荒野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值