文章目录
- QThread的API
 - 使用示例
 - 客户端多线程应用场景
 - 互斥锁
 
- QMutex
 - QMutexLocker
 - QReadWriteLocker、QReadLocker、QWriteLocker
 - 条件变量和信号量
 
QThread的API
Qt中的多线程和Linux中的线程,本质上是一个东西
Linux线程概念
Linux多线程——线程控制
Linux多线程——互斥锁
Linux多线程——生产消费者模型
QThread:
- 要创建线程,需要创建这个类的实例
 - 创建线程时,需指明线程入口函数
 - 创建
QThread的子类,重写了其中的run方法,起到指定入口函数的方式(多态) 
Tips:
这种方式在C++中并不常见,相比之下
std::thread直接指定回调方式更常见因为C++比较追求性能,多态机制可能导致运行时的额外开销(查询函数表,找到对应执行函数再执行)
但是对应客户端开发,对性能的要求,并没有那么的高
| API | 说明 | 
|---|---|
| run() | 线程入口函数 | 
| start() | 通过运行run()开始执行线程 (该操作是真正调用系统API创建线程)  | 
| currentThread() | 获取当前线程的指针 | 
| isRunning() | 如果线程正在运行返回true,否则返回false | 
| sleep()、msleep()、usleep() | 线程休眠,单位秒/毫秒/微妙 | 
| wait() | 线程阻塞,功能和pthread_join类似 | 
| terminate() | 终止线程执行。 线程可以立即终止,也可以不终止,取决于操作系统的调用  | 
| finished() | 线程结束发出的信号,可通过该信号实现线程的清理工作 | 
使用示例
基于定时器的倒计时程序
创建QThread子类:

thread.h:
#ifndef THREAD_H
#define THREAD_H
#include <QWidget>
#include<QThread>
class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();
    //重写父类run方法
    void run();
signals:
    //只需写函数声明, 定义Qt自动生成
    void notify();
};
#endif // THREAD_H
 
widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"thread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void handle();
private:
    Ui::Widget *ui;
    Thread thread;
};
#endif // WIDGET_H
 
thread.cpp:
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
    //针对时间进行计时,每过一秒,通过信号槽通知主线程更新界面
    for(int i = 0; i < 10; i++)
    {
        sleep(1);
        //发生信息
        emit notify();
    }
}
 
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //连接信号槽
    connect(&thread, &Thread::notify, this, &Widget::handle);
    //启动线程
    thread.start();
}
Widget::~Widget()
{
    delete ui;
}
void Widget::handle()
{
    int value = ui->lcdNumber->intValue();
    value--;
    ui->lcdNumber->display(value);
}
 
如果多个线程同时对界面进行修改,就会导致界面出错。
Qt直接一刀切,针对控件的任何修改,都在主线程中执行。
运行示意图:

客户端多线程应用场景
在服务器开发的角度,多线程主要是充分利用多核CPU的计算资源,达到更高的效率。
而对于客户端,对效率要求并不是特别高,如果追求效率,把CPU计算资源吃完,会导致系统卡顿,这用户体验是很差的。
在客户端中,多线程主要是用于一些耗时的等待IO的操作,避免主线程卡死。
比如说客户端向服务端上传/下载较大的文件
这种密集的IO操作会使程序被系统阻塞挂起,一旦进程被挂起了,此时用户的操作就无法响应了。
因此使用单独的线程来处理这种密集的IO操作,就算挂起,也是挂起的这个线程,并不会影响主线程。
互斥锁
QMutex
谈到线程,必定绕不开线程安全问题,最通用的手段就是加锁,QMutex类就是Qt封装的互斥锁。

上面这种情况就是线程安全问题,采取加锁,让线程串行执行
锁也是公共区的,只有一把锁

QMutexLocker
C++11引入了std::lock_guard,智能锁RAII机制,这样能避免抛出异常或者忘记释放锁导致的问题。
Qt参考过来了,叫做QMutexLocker。

Tips:
Qt的锁和C++的锁,本质上都是封装系统提供的锁
虽然可以用C++的锁锁住Qt的线程,但是不建议。
QReadWriteLocker、QReadLocker、QWriteLocker
QReadWriteLocker读写锁,用于控制读和写的并发访问QReadLocker用于读操作上锁,允许多个线程共享资源QWriteLocker用于写操作上锁,一次允许一个线程写数据
条件变量和信号量
Qt当中的条件变量和信号量,与Linux当中的概念一模一样,只不过是接口不一样而已。
多个线程的调度是无序的,为了一定程度干预执行顺序,引入条件变量。
QWaitCondition:
wait等待wake唤醒wakeAll唤醒全部
QSemaphore:
acquire获取信号量release释放信号量



















