- 👋 Hi, I’m @Peakchen
- 👀 I’m interested in ...
- 🌱 I’m currently learning ...
- 💞️ I’m looking to collaborate on ...
- 📫 How to reach me ...
peakchen / blog Goto Github PK
View Code? Open in Web Editor NEWDataBase/Qt/Linux/C++/C
License: MIT License
DataBase/Qt/Linux/C++/C
License: MIT License
一、Qt提供的多线程
1.1 重要概念
主线程 的 入口函数:main()
子线程 的 入口函数:run()
任何进程 至少有1个 主线程
1.2 何时需要手工建立 子线程
案例1:----有一个聊天软件客户端,点击LOOP按钮进入了死循环(如,反复打印hello);但这时要求,主界面不能被冻结,仍能正常进行聊天操作
案例2:----卖火车票。有100张火车票,通过2个窗口同时卖,要求不能有重号、缺号的问题。
1.3 在Qt中,如何定义入口函数run
步骤:
1、自定义1个类,如LoopThread,基类必须填QThread
2、在h文件中,增加函数原型void run()
3、在cpp文件中,定义run函数,代码如下
void LoopThread::run()
{
int i=0;
while(true)
{
//QString::number(i);
qDebug()<<"this is loop"+ QString::number(i)<<endl;
i++;
}
return;
}
1.4 在Qt中,如何使用子线程
案例:----在主界面,点击按钮后,会建立1个子线程,在子线程中将循环进行打印;主界面不会被冻结
步骤:
1、点击按钮右键----Go to Slot
2、填写代码,如下:
void Chat001Widget::on_pushButtonLoop_clicked()
{
LoopThread *loopThread=new LoopThread; //建立 自定义类的对象
loopThread->start(); //调用start成员函数(它的父类是QThread),它会自动调用子线程的入口函数run
}
二、Qt中的线程同步
案例----卖火车票。有100张火车票,通过2个窗口同时卖,要求不能有重号、缺号的问题。
编程思路:
1、在主界面上,有1个按钮,点击该按钮后,会触发1个函数。该函数将建立2个子线程,每个子线程都是循环打印ticket的值,然后又让ticket--,直到ticket等于0
2、定义int ticket=100,而且为全局变量,便于run函数直接读写。----全局变量不好,我们以后再改进代码
3、定义QMutex mutex,而且为全局变量(对象),便于run函数直接读写。----全局变量不好,我们以后再改进代码
4、其它文件,如何要读写全局变量,必须使用extern进行声明
编程步骤:
1、在主界面的按钮上点击右键----选Go to Slot
void Ticket01Widget::on_pushButton_clicked()
{
Admin *admin1=new Admin; //建立第1个售票员对象
Admin *admin2=new Admin; //建立第2个售票员对象
admin1->start(); //第1个售票员开始工作,她将连续打印还未售出的票
admin2->start(); //第2个售票员开始工作,她将连续打印还未售出的票
}
说明:
1、这仍然是自顶而下的设计思路;先搭好框架,后面再"求精",即定义Admin类 及其成员函数run
2、在该cpp文件的开头,定义2个全局变量
int ticket=800;
QMutex mutex;
2、自定义类Admin,基类必须为QThread
//h文件
#ifndef ADMIN_H
#define ADMIN_H
#include
class Admin : public QThread
{
public:
Admin();
void run();
};
#endif // ADMIN_H
//cpp文件:
#include "admin.h"
extern int ticket;
extern QMutex mutex;
Admin::Admin()
{
}
void Admin::run()
{
while(true)
{
mutex.lock();
if(ticket>0)
{
Qt::HANDLE handle=currentThreadId(); //获得线程ID号
qDebug()<<handle<<" "<<ticket;
ticket--;
}
else
{
mutex.unlock();
break;
}
mutex.unlock();
}
return;
}
三、QMutex类
中文为 互斥量。
调用它的成员函数lock,即加锁;加锁后,别的线程就不能再工作了;但加锁时,如果锁已被别的线程占用,则该函数就会阻塞在这里,一直等锁。
调用它的成员函数unlock,即解锁;解锁后,别的线程才能得到锁,并继续工作;但解锁时,如果本线程并没有加锁,则会报错。
本例中,QMutex类的对象mutex,为全局变量,也是唯一的;以后可以改进代码,不使用全局变量。
注意:
加锁、解锁的地方要合理;如本例,应放在while循环里面
一、主线程 子线程 之间的通信
子线程 向 主线程 传递数据
案例:----将子线程中的字符串,如"hello,bmd",传给主线程的 LineEdit行编辑器
思路:
1、点击界面上的1个按钮,应可以触发建立1个 子线程
2、在子线程运行时,子线程需要发送1个信号,这个信号是自定义的信号,信号参数就是字符串"hello,bmd"
3、主线程中的界面类,可以捕获到该信号,而且捕获后,会自动触发调用1个槽函数,如modifyLineEdit(QString str)
4、在主线程中的界面类里,槽函数modifyLineEdit(QString str)可以修改行编辑器中的内容
设计界面:增加1个按钮 1个行编辑器
代码:
//主界面的h文件
#ifndef COMMTHREADWIDGET_H
#define COMMTHREADWIDGET_H
#include
#include
class TestThread;
namespace Ui
{
class CommThreadWidget;
}
class CommThreadWidget : public QWidget
{
Q_OBJECT
public:
CommThreadWidget(QWidget *parent = 0);
~CommThreadWidget();
private:
Ui::CommThreadWidget *ui;
TestThread *testThread; //增加了1个成员变量,为线程类的地址
private slots:
void on_pushButton_clicked(); //点击按钮后,会执行的槽函数
void modifyLineEdit(QString str); //定义的槽函数,可以修改行编辑器的内容
};
#endif // COMMTHREADWIDGET_H
//主界面的cpp文件
#include "commthreadwidget.h"
#include "ui_commthreadwidget.h"
#include "testthread.h"
CommThreadWidget::CommThreadWidget(QWidget *parent)
: QWidget(parent), ui(new Ui::CommThreadWidget)
{
ui->setupUi(this);
testThread=new TestThread; //建立了1个线程类对象,并对象地址赋值给成员变量testThread
connect(testThread,SIGNAL(signalFun(QString)),this,SLOT(modifyLineEdit(QString))); //连接信号和槽,注意,发送者和接受者如何确定
}
CommThreadWidget::~CommThreadWidget()
{
delete ui;
}
void CommThreadWidget::on_pushButton_clicked()
{
//testThread=new TestThread;
testThread->start(); //调用该函数后,run函数会被自动调用,子线程就建立起来了。
}
void CommThreadWidget::modifyLineEdit(QString str)
{
ui->lineEdit->setText(str); //修改行编辑器的内容
return;
}
//线程类的h文件
#ifndef TESTTHREAD_H
#define TESTTHREAD_H
#include
class TestThread : public QThread
{
Q_OBJECT
public:
TestThread();
void run();
signals:
void signalFun(QString str); //在线程类中 增加了该信号,信号参数就是要传递的字符串
};
#endif // TESTTHREAD_H
//线程类的cpp文件
#include "testthread.h"
TestThread::TestThread()
{
}
void TestThread::run()
{
//qDebug()<<"hi,zhang";
QString str="hello,bmd";
emit signalFun(str); //子线程运行时,使用emit发送了1个信号
return;
}
二、主线程 子线程 之间的通信
主线程 向 子线程 传递数据
案例:----将主线程中,LineEdit行编辑器上的字符串,如"hello,world",传递给子线程
思路:
1、点击主界面上的按钮A,将触发建立和运行1个子线程
2、界面上还有1个LineEdit行编辑器 和 按钮B。点击按钮B后,可以将行编辑器上的文本读出来,然后发送1个自定义信号signalFun(QString str),信号参数就是前面读出的文本
3、正在运行的子线程,收到信号后,会触发调用1个槽函数print_str(QString str),而该槽函数的功能就是打印 信号中的字符串"hello,world"
关键点:
1、默认情况下,TestThread类中的槽函数print_str(QString str) 只会在主线程中运行;
2、因此要将槽函数 移到另外1个独立的类中,如Student类
3、Student类的对象,应该在主线程中建立,但此时Student类中的槽函数print_str(QString str),仍然只会在主线程中运行;
4、因此,需要使用Student类的成员函数moveToThread(其实是父类QObject的成员函数),将stu对象搬移到 子线程中去
5、之后,Student类中的所有函数,都会在子线程中运行
6、自定义的信号、槽、connect用法 不变
代码:
//主界面的h文件
#ifndef COMMTHREAD002WIDGET_H
#define COMMTHREAD002WIDGET_H
class TestThread;
class Student;
#include
#include
namespace Ui
{
class CommThread002Widget;
}
class CommThread002Widget : public QWidget
{
Q_OBJECT
public:
CommThread002Widget(QWidget *parent = 0);
~CommThread002Widget();
private:
Ui::CommThread002Widget *ui;
TestThread *testThread; //线程类的地址
Student *stu; //独立类的地址
private slots:
void on_pushButtonSend_clicked(); //点击发送按钮 触发的槽函数
void on_pushButtonRunThread_clicked(); //点击建立线程按钮 触发的槽函数
signals:
void signalFun(QString str); //自定义的信号
};
#endif // COMMTHREAD002WIDGET_H
//主界面的cpp文件
#include "commthread002widget.h"
#include "ui_commthread002widget.h"
#include "testthread.h"
#include "student.h"
CommThread002Widget::CommThread002Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::CommThread002Widget)
{
ui->setupUi(this);
testThread=new TestThread;
stu=new Student;
connect(this,SIGNAL(signalFun(QString)),stu,SLOT(print_str(QString))); //将自定义的信号、槽 连接起来
}
CommThread002Widget::~CommThread002Widget()
{
delete ui;
}
void CommThread002Widget::on_pushButtonRunThread_clicked()
{
qDebug()<<"main threadID:"<<QThread::currentThreadId();
stu->moveToThread(testThread); //关键语句。执行后,stu就进入到子线程了;本质上,以后学生类中的槽函数,都将在子线程中运行
testThread->start();
}
void CommThread002Widget::on_pushButtonSend_clicked()
{
QString str=ui->lineEdit->text();
emit signalFun(str); //主线程 发射 信号
}
//线程类的h文件
#ifndef TESTTHREAD_H
#define TESTTHREAD_H
#include
class TestThread : public QThread
{
Q_OBJECT
public:
TestThread();
void run();
//public slots:
//void print_str(QString str);
};
#endif // TESTTHREAD_H
//线程类的cpp文件
#include "testthread.h"
TestThread::TestThread()
{
}
void TestThread::run()
{
qDebug()<<"hi,world";
exec(); //必须要有该语句,这样才会进入事件循环;否则,执行到return,子线程就退出了,因此,子线程中的槽函数根本不会被执行。
return;
}
//学生类(独立类)的h文件
#ifndef STUDENT_H
#define STUDENT_H
#include
class Student : public QObject
{
Q_OBJECT
public:
Student();
public slots:
void print_str(QString str);
};
#endif // STUDENT_H
//学生类的cpp文件
#ifndef STUDENT_H
#define STUDENT_H
#include
class Student : public QObject
{
Q_OBJECT
public:
Student();
public slots:
void print_str(QString str);
};
一、多线程
QWaitCondition类
1.1 QWaitCondition类的使用
案例:----书店管理员给货架上放书,学生来买书,二者同时进行
思路:
1、让书店管理员连续放100本书,但书架上只能容下20本书,因此,当书架放满
书后,书店管理员必须等待,等待学生(购书者)买走1本或多本书
2、学生,即购书者连续买10本书,但书架上没有书时,学生必须等待,等待书
店管理员放1本或多本书到书架上
主界面的cpp文件:
#include "waitcondition001widget.h"
#include "ui_waitcondition001widget.h"
#include "adminthread.h"
#include "studentthread.h"
int numBook=0; //代表书架上有多少本书,初始时没有书
QMutex mutex; //定义1个锁,当书店管理员、学生 查询和修改numBook时,
要先加锁
QWaitCondition waitCondition; //这是等待条件类的对象,它一般要和QMutex联合使
用;它可以解决 睡眠----唤醒 的问题
WaitCondition001Widget::WaitCondition001Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::WaitCondition001Widget)
{
ui->setupUi(this);
}
WaitCondition001Widget::~WaitCondition001Widget()
{
delete ui;
}
void WaitCondition001Widget::on_pushButton_clicked()//主界面上,点
击按钮后,将调用该函数
{
StudentThread *studentThread=new StudentThread;//建立1个学生
类
AdminThread *adminThread=new AdminThread; //建立1个书店
管理员类
studentThread->start(); //将建立和运行1个子线程,模拟
学生连续买10本书的情况
adminThread->start(); //将建立和运行1个子线程,模拟
书店管理员连续在书架上放100本书的情况
}
//书店管理员的h文件
#include
class AdminThread : public QThread
{
public:
AdminThread();
void run();
};
//书店管理员的cpp文件
#include "adminthread.h"
extern int numBook;
extern QMutex mutex;
extern QWaitCondition waitCondition;
AdminThread::AdminThread()
{
}
void AdminThread::run()
{
for(int i=0;i<100;i++)
{
mutex.lock();
if(numBook==20) //条件成立时,说明书架上的书放满了;当前线程应
该睡眠,把cpu交给其它线程运行;但睡眠前,应该先要解锁
{
mutex.unlock(); //解锁
waitCondition.wait(&mutex); //当前线程睡眠
continue;
}
else
{
qDebug()<<"书店管理员放好了1本书"+QString::number(i);
numBook++; //书架上多了1本书,所以要执行
该语句
waitCondition.wakeOne(); //书架上的书增加了,因此,可以唤醒其它
睡眠的线程了。----注意,是随机唤醒了1个线程。
}
mutex.unlock(); //每循环1次,应解锁1次;这样别的线程得到时间片
后,也可以加解锁了。
sleep(2); //本线程睡2秒,而且时间片还会切换到其
它线程
}
return;
}
//学生(购书者)的h文件
#include
class StudentThread : public QThread
{
public:
StudentThread();
void run();
};
//学生(购书者)的cpp文件
#include "studentthread.h"
extern int numBook;
extern QMutex mutex;
extern QWaitCondition waitCondition;
StudentThread::StudentThread()
{
}
void StudentThread::run()
{
for(int i=0;i<10;i++)
{
mutex.lock();
if(numBook==0)
{
mutex.unlock();
waitCondition.wait(&mutex);
continue;
}
else
{
qDebug()<<"学生拿到了1本书"+QString::number(i);
numBook--;
waitCondition.wakeOne();
}
mutex.unlock();
sleep(2);
}
return;
}
1.2 上述代码的改进
上述代码存在的问题:----以书店线程为例
1、一旦if语句中的条件成立,则当前的i就不会被打印出来了,因为会越过else
中的打印
原来的代码:
void AdminThread::run()
{
for(int i=0;i<100;i++)
{
mutex.lock();
if(numBook==20) //条件成立时,说明书架上的书放满了;当前线程应
该睡眠,把cpu交给其它线程运行;但睡眠前,应该先要解锁
{
mutex.unlock(); //解锁
waitCondition.wait(&mutex); //当前线程睡眠
continue;
}
else
{
qDebug()<<"书店管理员放好了1本书"+QString::number(i);
numBook++; //书架上多了1本书,所以要执行
该语句
waitCondition.wakeOne(); //书架上的书增加了,因此,可以唤醒其它
睡眠的线程了。----注意,是随机唤醒了1个线程。
}
mutex.unlock(); //每循环1次,应解锁1次;这样别的线程得到时间片
后,也可以加解锁了。
sleep(2); //本线程睡2秒,而且时间片还会切换到其
它线程
}
return;
}
改进后的代码:
void AdminThread::run()
{
for(int i=0;i<100;i++)
{
mutex.lock();
while(numBook==20)
{
mutex.unlock();
waitCondition.wait(&mutex);
mutex.lock();
//continue;
}
qDebug()<<"书店管理员放好了1本书"+QString::number(i);
numBook++;
waitCondition.wakeOne();
mutex.unlock();
msleep(2);
}
return;
}
类似的,学生(购书者)的run函数也要改进为:
void StudentThread::run()
{
for(int i=0;i<10;i++)
{
mutex.lock();
while(numBook==0)
{
mutex.unlock();
waitCondition.wait(&mutex);
mutex.lock();
//continue;
}
qDebug()<<"学生拿到了1本书"+QString::number(i);
numBook--;
waitCondition.wakeOne();
mutex.unlock();
msleep(2);
}
return;
}
1.3 上述代码的另外1种用法:
void AdminThread::run()
{
for(int i=0;i<100;i++)
{
mutex.lock();
while(numBook==20)
{
//mutex.unlock(); //注释掉该行
waitCondition.wait(&mutex);
//mutex.lock(); //注释掉该行 能注释
掉这2行的原因是:wait函数会在wait前自动unlock1次,wait后自动lock1次,因此可以
省这2行语句。
//continue; //但推荐不要省,这样可
以增加代码的可读性,即wait前要unlock,否则别的线程得到时间片后,得不到锁,还是
干不了啥
}
qDebug()<<"书店管理员放好了1本书"+QString::number(i);
numBook++;
waitCondition.wakeOne();
mutex.unlock();
msleep(2);
}
return;
}
一、pthread线程函数库
1.1 概述
Linux下的线程函数库
Windows下没有
因此使用pthread函数的程序,只能在Linux下运行。
比较:
Qt下,有自己的多线程机制----QThread类下的run函数----子线程入口
函数
而Qt是跨平台的,所以使用QThread类的程序,能在Linux下、Windows
下运行(但注意,是源码级的跨平台,仍然需要在各自的平台下编译)
1.2 查询当前线程的 线程id号
使用pthread_self()函数,返回值为pthread_t类型的 线程id号
1.3 建立子线程的函数
pthread_create()
有4个参数:
1、是线程id的地址,用来保存新建立的 线程id号,因此是 输出参数
2、线程属性,使用默认值NULL即可
3、子线程的入口函数,需要填写1个函数名。 函数名可以任意,如
run,但参数必须是void *,返回值类型也必须是void *
1.4 线程同步----使用mutex互斥量
使用步骤:
1、定义1个全局变量mutex,并赋值
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
2、当子线程读写 共享资源(如火车票)时,先加锁
3、读写完毕后,尽量早点 解锁----为了让其它线程运行时,可以得到锁
代码:----2个线程,同时买火车票的例子
#include <QtCore/QCoreApplication>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; //定义并初始化1个锁
mutex 注意是全局变量
int ticket=100; //共享资源 100张火车票
void * run(void *arg) //子线程的入口函数----注意 函数名是可以任意定的;但参
数、返回值类型必须是void *
{
for(int i=0;i<50;i++)
{
pthread_mutex_lock(&mutex); //加锁,使用函数pthread_mutex_lock,参数是锁的地址
if(ticket==0)
{
break;
}
printf("tid %u ticketNum %d\n",pthread_self(),ticket);
ticket--;
pthread_mutex_unlock(&mutex); //解锁,使用函数pthread_mutex_unlock,参数是锁的地址
usleep(2000); //睡眠函数,单位是微妙;而且会导致cpu先运行其它线程;需要头文件unistd.h
}
return NULL;
}
int main(int argc, char *argv[])
{
//QCoreApplication a(argc, argv);
//pthread_t tid=pthread_self();
//printf("main tid is %u\n",tid);
pthread_t tid2=0;
pthread_t tid3=0;
pthread_create(&tid2,NULL,run,NULL); //建立线程,注意将新的线程id号,赋值给了tid3
pthread_create(&tid3,NULL,run,NULL);
sleep(3); //sleep函数,单位是秒;而且会导致cpu先运行其它线程,间接保证了main函数不要退出太早。sleep需要头文件unistd.h
return 0;
//return a.exec();
}
1.5 当前线程开始等待,直到等待的另外1个线程退出,然后当前线程才能继续向下运行
pthread_join函数 ----有上述功能
注意:利用该函数,在main函数中,可以不使用sleep了
提示:
1、建立4个线程,模拟4个哲学家不停的吃饭、休息
2、建立4个锁,对应了4个筷子,线程要通过对 不同的锁 加锁/解锁 来决定是
否可以吃上饭
代码:
//主界面的cpp文件
#include "eat001widget.h"
#include "ui_eat001widget.h"
#include "persona.h"
#include "personb.h"
#include "personc.h"
#include "persond.h"
QMutex mutex1; //建立了4个锁,模拟了4个筷子
QMutex mutex2;
QMutex mutex3;
QMutex mutex4;
Eat001Widget::Eat001Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Eat001Widget)
{
ui->setupUi(this);
}
Eat001Widget::~Eat001Widget()
{
delete ui;
}
void Eat001Widget::on_pushButton_clicked() //点击界面上的按钮后,
4个线程开始工作
{
PersonA * personA=new PersonA;
PersonB * personB=new PersonB;
PersonC * personC=new PersonC;
PersonD * personD=new PersonD;
personA->start();
personB->start();
personC->start();
personD->start();
}
//PersonA类的h文件
#ifndef PERSONA_H
#define PERSONA_H
#include
class PersonA : public QThread
{
public:
PersonA();
void run();
};
#endif // PERSONA_H
//PersonA类的cpp文件
#include "persona.h"
extern QMutex mutex1;
extern QMutex mutex4;
PersonA::PersonA()
{
}
void PersonA::run()
{
while(true)
{
mutex1.lock(); //代表取1号筷子
qDebug()<<"A +1";
msleep(1);
mutex4.lock(); //代表取4号筷子
qDebug()<<"A +4";
qDebug()<<"A is eatting"; //取到2个筷子,才能吃饭
mutex4.unlock(); //代表放下4号筷子
qDebug()<<"A -1";
mutex1.unlock(); //代表放下1号筷子
qDebug()<<"A -4";
//wait(1);
msleep(1); //等待1毫秒后,又去取筷子、吃饭
}
return;
}
void PersonB::run()
{
while(true)
{
mutex1.lock();
qDebug()<<"B +2";
mutex2.lock();
qDebug()<<"B +1";
qDebug()<<"B is eatting";
mutex1.unlock();
qDebug()<<"B -2";
mutex1.unlock();
qDebug()<<"B -1";
//wait(1);
msleep(1);
}
return;
}
void PersonC::run()
{
while(true)
{
mutex2.lock();
qDebug()<<"C +3";
mutex3.lock();
qDebug()<<"C +2";
qDebug()<<"C is eatting";
mutex3.unlock();
qDebug()<<"C -3";
mutex2.unlock();
qDebug()<<"C -2";
//wait(1);
msleep(1);
}
return;
}
void PersonD::run()
{
while(true)
{
mutex3.lock();
qDebug()<<"D +4";
mutex4.lock();
qDebug()<<"D +3";
qDebug()<<"D is eatting";
mutex4.unlock();
qDebug()<<"D -4";
mutex3.unlock();
qDebug()<<"D -3";
//wait(1);
msleep(1);
}
return;
}
分析:----死锁问题
要顺序加锁、反序解锁,否则可能会出现死锁
而且加锁时,都是先加序号小的锁,再加序号大的锁
一、建立子线程的程序,可以不用 自定义QThread的派生类
案例:
原来,我们自定义了1个TestThread,它的基类是QThread
现在,不需要了
解决步骤:
1、在主线程中,直接使用new建立QThread对象,如testThread
2、建立1个独立的类,如Student类,但基类必须是QObject
3、在主线程中,new 1个Student对象,如stu,然后,使用stu->moveToThread(testThread)函数,将Student对象放到子线程
4、testThread->start(),启动1个子线程
二、停止子线程的方法
步骤如下:
testThread->quit(); //通知子线程,可以退出了
testThread->wait(); //当前线程会阻塞,一直等,等到子线程退出了,才不阻塞了,程序才会向下执行
testThread->deleteLater(); //删除延迟,更安全的用法
delete testThread; //可选:因为前面用new建立的对象,因此,这里可能需要使用delete删除
testThread=NULL; //可选:虽然delete了对象,但testThread本身是地址变量,它存的是地址,仍然没有变。为例安全,最好重新赋值为0
三、如何检查某个子线程的状态
isRunning
isFinished
四、子线程 自动发射的信号
started()
finnished()
五、代码:----不需要testThread类了
//主界面的h文件
#ifndef COMMTHREAD002WIDGET_H
#define COMMTHREAD002WIDGET_H
//class TestThread;
class Student;
#include
#include
namespace Ui
{
class CommThread002Widget;
}
class CommThread002Widget : public QWidget
{
Q_OBJECT
public:
CommThread002Widget(QWidget *parent = 0);
~CommThread002Widget();
private:
Ui::CommThread002Widget *ui;
QThread *testThread; //使用了基类QThread,不需要自定义TestThread类了
Student *stu;
private slots:
void on_pushButtonStopThread_clicked();
void on_pushButtonSend_clicked();
void on_pushButtonRunThread_clicked();
void slotReceiveStarted(); //手动增加的,当子线程运行时,会发射信号stated();利用该槽函数,可以进行打印和验证
void slotReceiveFinished(); //手动增加的,当子线程停止运行时,会发射信号finished();利用该槽函数,可以进行打印和验证
signals:
void signalFun(QString str);
};
#endif // COMMTHREAD002WIDGET_H
//主界面的cpp文件
#include "commthread002widget.h"
#include "ui_commthread002widget.h"
#include "testthread.h"
#include "student.h"
CommThread002Widget::CommThread002Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::CommThread002Widget)
{
ui->setupUi(this);
testThread=new QThread;
stu=new Student;
connect(this,SIGNAL(signalFun(QString)),stu,SLOT(print_str(QString)));
connect(testThread,SIGNAL(started()),this,SLOT(slotReceiveStarted())); //连接信号--槽
connect(testThread,SIGNAL(finished()),this,SLOT(slotReceiveFinished())); //连接信号--槽
}
void CommThread002Widget::slotReceiveStarted()
{
qDebug()<<"receive started";
}
void CommThread002Widget::slotReceiveFinished()
{
qDebug()<<"receive finished";
}
CommThread002Widget::~CommThread002Widget()
{
delete ui;
}
void CommThread002Widget::on_pushButtonRunThread_clicked()
{
qDebug()<<"main threadID:"<<QThread::currentThreadId();
stu->moveToThread(testThread);
testThread->start();
}
void CommThread002Widget::on_pushButtonSend_clicked()
{
QString str=ui->lineEdit->text();
emit signalFun(str);
}
void CommThread002Widget::on_pushButtonStopThread_clicked() //点击停止子线程按钮,触发该槽函数
{
//qDebug()<isRunning(); //打印 该子线程是否在运行
qDebug()<isFinished(); //打印 该子线程是否已结束
testThread->quit(); //通知子线程 可以退出了
testThread->wait(); //一直等待,直到 该子线程退出
qDebug()<isFinished(); //打印 该子线程是否已结束
testThread->deleteLater(); //更安全的用法
delete testThread;
testThread=NULL;
}
//独立类Student的h文件
#ifndef STUDENT_H
#define STUDENT_H
#include
class Student : public QObject
{
Q_OBJECT
public:
Student();
public slots:
void print_str(QString str);
};
#endif // STUDENT_H
//独立类Student的cpp文件
#include "student.h"
Student::Student()
{
}
void Student::print_str(QString str)
{
qDebug()<<QThread::currentThreadId()<<" "<<str;
return;
}
六、重要技巧
1、如果使用QThread的派生类,如TestThread
则在该类的h文件中,只能增加信号,不要增加槽;否则,很容易出现问题,代码也不规范
2、如果要求,子线程A 给 子线程B 发送数据
要利用信号--槽 机制
可以不自定义QThread的派生类
建立2个独立的类,但基类必须是QObject
在2个独立的类中,可以增加 信号、槽
使用moveToThread函数,将2个独立的类对象,移到各自的子线程中
信号--槽 用法不变
就可以进行任意的子线程间 数据传递了
一、线程同步之 信号量使用Qt中的QSemaphore类
案例:----人放置麦粒 小鸡吃麦粒 同时进行
思路:
1、有20个食品盒子,每个盒子只能放置1个麦粒
2、让人连续放置100个麦粒,但当20个食品盒子都放置了麦粒时,要暂停,需要等小鸡吃掉1个或多个麦粒
3、让小鸡连续吃100个麦粒,但当20个食品盒子都没有麦粒时,要暂停,需要等人放置1个或多个麦粒
编程步骤:
1、点击界面按钮的右键----选取Go to Slot
代码:
void Semaphore001Widget::on_pushButton_clicked()
{
PersonThread *personThread=new PersonThread;
ChickThread *chickThread=new ChickThread;
personThread->start();
chickThread->start();
}
注意:
在文件中,定义了2个全局变量
QSemaphore semaphoreFree(20); //表示空闲的食品盒子有20个,即没有麦粒
QSemaphore semaphoreUsed(0); //表示有麦粒的食品盒子有0个
2、补充PersonThread、ChickThread两个类,基类必须是QThread
//PersonThread的h文件:
#include
class PersonThread : public QThread
{
public:
PersonThread();
void run();
};
//PersonThread的cpp文件:
#include "personthread.h"
extern QSemaphore semaphoreFree; //声明变量semaphoreFree的类型,由于是在其它文件中定义的,因此在这里要加extern
extern QSemaphore semaphoreUsed;
PersonThread::PersonThread()
{
}
void PersonThread::run()
{
for(int i=0;i<100;i++)
{
semaphoreFree.acquire(); //该函数执行后,会让空闲的食品盒子数会减少1;但如果20个食品盒子都有麦粒时,该函数会阻塞,cpu会去执行其它线程;其它线程执行完后,仍然会返回到这里
qDebug()<<"人已经放置了1粒麦子"+QString::number(i);
semaphoreUsed.release(); //该函数执行后,会让已使用(即 有麦粒)食品盒子数增加1;
msleep(2); //睡眠2ms;同时cpu会去执行其它线程;其它线程执行完后,仍会返回到这里,继续。
}
return;
}
//ChickThread类的h文件:
#include
class ChickThread : public QThread
{
public:
ChickThread();
void run();
};
//ChickThread类的cpp文件:
#include "chickthread.h"
extern QSemaphore semaphoreFree;
extern QSemaphore semaphoreUsed;
ChickThread::ChickThread()
{
}
void ChickThread::run()
{
for(int i=0;i<100;i++)
{
semaphoreUsed.acquire();
qDebug()<<"小鸡已经吃了1粒麦子"+QString::number(i);
semaphoreFree.release();
msleep(2);
}
return;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.