C++多线程编程指南(菜鸟秒懂版)(c++多线程编程实战 pdf)

yumo6663个月前 (08-18)技术文章63

一、为什么需要多线程?

生活场景:把程序想象成餐厅

  • 单线程:只有一个服务员,既要点菜又要上菜 → 顾客等得抓狂
  • 多线程:服务员(主线程)负责接待,厨师(子线程)后台做菜 → 顾客体验流畅

卡界面示例

// 错误示范:在主线程执行耗时操作 
void 处理文件() {
    // 模拟耗时操作(5秒)
    std::this_thread::sleep_for(std::chrono::seconds(5)); 
}

点击按钮时调用处理文件(); // 界面会冻结5秒

二、多线程基础概念

1. 线程创建(雇佣厨师)

C++标准库方法(std::thread)

#include <thread>

void 后台任务() {
    std::cout << "子线程开始工作..." << std::endl;
    // 耗时操作...
}

int main() {
    std::thread 厨师(后台任务); // 雇佣厨师
    厨师.join();  // 等待厨师完成
    return 0;
}

Qt框架方法(QThread)

class Worker : public QObject {
    Q_OBJECT
public slots:
    void 执行任务() {
        // 耗时操作...
        emit 任务完成();
    }
signals:
    void 任务完成();
};

// 使用
QThread *子线程 = new QThread;
Worker *工人 = new Worker;
工人->moveToThread(子线程);
connect(子线程, &QThread::started, 工人, &Worker::执行任务);
子线程->start();

三、不卡界面的核心方案

1. 主线程与子线程分工

主线程(服务员)

子线程(厨师)

处理界面交互

执行耗时计算

更新UI控件

文件读写/网络请求

响应按钮点击

图像处理/大数据分析


2. 跨线程通信(对讲机系统)

Qt信号槽跨线程通信

class Controller : public QObject {
    Q_OBJECT
public slots:
    void 更新进度(int 百分比) {
        progressBar->setValue(百分比); // 安全更新UI
    }
};

// 子线程发送信号
emit 更新进度(50); 

// 连接信号槽(自动跨线程)
connect(工人, &Worker::更新进度, 控制器, &Controller::更新进度);

四、线程安全注意事项

1. 数据竞争(多人抢厨房)

int 全局计数器 = 0;

void 不安全增加() {
    for(int i=0; i<10000; ++i) 
        全局计数器++; // 多个线程同时操作会出错
}

// 正确做法:加锁
std::mutex 厨房锁;
void 安全增加() {
    std::lock_guard<std::mutex> 锁(厨房锁);
    for(int i=0; i<10000; ++i)
        全局计数器++;
}

2. 界面更新原则

  • 黄金法则:只能在主线程更新UI控件

错误示例

// 子线程中直接更新(会崩溃!)
void Worker::执行任务() {
    label->setText("完成"); // 危险操作
}

正确做法

// 通过信号槽通知主线程
emit 更新UI请求("完成"); 

// 主线程槽函数
void Controller::处理UI更新(QString 文本) {
    label->setText(文本); // 安全
}

五、完整案例:文件搜索工具

1. 界面设计

  • 搜索路径输入框
  • 开始按钮
  • 进度条
  • 结果列表

2. 核心代码

// Worker类
class FileSearcher : public QObject {
    Q_OBJECT
public slots:
    void 开始搜索(QString 路径) {
        QDirIterator 迭代器(路径, QDir::Files, QDirIterator::Subdirectories);
        while(迭代器.hasNext()) {
            if(停止标记) break;
            emit 找到文件(迭代器.next());
            emit 更新进度(计算进度());
        }
        emit 搜索完成();
    }
signals:
    void 找到文件(QString);
    void 更新进度(int);
    void 搜索完成();
};

// 主线程连接
connect(开始按钮, &QPushButton::clicked, [=]{
    工人->停止标记 = false;
    子线程->start();
});

connect(工人, &FileSearcher::找到文件, 结果列表, [=](QString 文件){
    结果列表->addItem(文件);
});

六、常见问题解答

1. 程序崩溃怎么办?

检查项

  1. 是否跨线程访问了UI控件?
  2. 线程对象生命周期是否管理正确?
  3. 信号槽连接方式是否正确?

2. 如何停止正在运行的线程?

// 设置停止标志
volatile bool 停止标记 = false; // volatile确保可见性

void Worker::执行任务() {
    while(!停止标记) {
        // 执行任务...
    }
}

// 点击停止按钮时
停止标记 = true;

3. 多线程调试技巧

日志输出

qDebug() << "[子线程]" << QThread::currentThreadId();

断点调试

七、性能优化建议

场景

优化方案

效果

频繁创建线程

使用线程池(QThreadPool)

减少线程创建开销

大量小任务

使用QtConcurrent::run

自动分配任务

数据共享频繁

使用无锁数据结构(原子操作)

避免锁竞争

线程池示例

// 创建10个线程的池子
QThreadPool::globalInstance()->setMaxThreadCount(10);

// 提交任务
QtConcurrent::run([]{
    // 自动分配线程执行
});

终极口诀
主线程管界面,耗时操作放后台
信号槽传消息,加锁保护共享数据
线程安全记心间,界面更新走信号
遇到崩溃莫慌张,检查跨线程访问
线程池用起来,性能提升看得见!

相关文章

Qt多线程创建(qt多线程直接处理数据)

【为什么要用多线程?】传统的图形用户界面应用程序都只有一个执行线程,并且一次只执行一个操作。如果用户从用户界面中调用一个比较耗时的操作,当该操作正在执行时,用户界面通常会冻结而不再响应。这个问题可以用...

Qt 的4种多线程实现详解(qt实现多线程文件传输)

为何需要多线程?1、进行耗时操作时,可以处理用户的其他输入输出。比如,如果在UI线程里面进行耗时操作,界面会不响应用户操作。2、提升程序性能。现在的电脑一般都是多核CPU,多线程并行处理事务,可以大大...

Qt多线程1:QThread(Qt多线程通信)

1. Qt多线程概述Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。 Qt4.8之前都是使用继承QThread的ru...

从零开始学Qt(76):什么是多线程?(怎么从0开始学编程)

多线程程序一个应用程序一般只有一个线程,一个线程内的操作是顺序执行的,如果有某个比较消耗时 间的计算或操作,比如网络通信中的文件传输,在一个线程内操作时,用户界面就可能会冻结而不能及时响应。这种情况下...

Qt 多线程之QThreadPool(qt多线程movetothread)

上述的方法需要手动建立线程,如果并发线程数量较多,而且每个线程只执行较短的任务就结束,这样就会导致线程的频繁创建和销毁,从而降低效率。因此给出另外一种做法--线程池QThreadPool。线程池的优点...

基于Qt多线程实现UDP通信(qt实现多线程文件传输)

# 演示先演示,在展开如何实现,本次代码在windows、linux、mac都可使用。大家不能光看,实际敲一敲,敲出强大,敲出好工作。简单理解:服务器-》发送hello-》客户端也可以不写客户端或者服...