Giter VIP home page Giter VIP logo

blog's Introduction

  • 👋 Hi, I’m @Peakchen
  • 👀 I’m interested in ...
  • 🌱 I’m currently learning ...
  • 💞️ I’m looking to collaborate on ...
  • 📫 How to reach me ...

blog's People

Contributors

peakchen avatar

Watchers

 avatar  avatar

blog's Issues

Qt之 多线程(一)

一、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循环里面

Qt之多线程(四)

一、主线程 子线程 之间的通信
子线程 向 主线程 传递数据

案例:----将子线程中的字符串,如"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);
};

Qt之多线程(三)

一、多线程

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;
}

Qt之多线程(六)

一、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了

二、哲学家就餐问题---考察对线程死锁的理解

描述:
有4个哲学家一起吃饭,大家围成一圈;但每个哲学家之间都只有1个筷子。
哲学家要不停得休息、吃饭;
吃饭时,要依次拿自己2侧的筷子,如果拿不到,就死等;
休息时,要依次放下左右2侧的筷子。

提示:
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;  

}

分析:----死锁问题
要顺序加锁、反序解锁,否则可能会出现死锁
而且加锁时,都是先加序号小的锁,再加序号大的锁

Qt之多线程(五)

一、建立子线程的程序,可以不用 自定义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之 多线程(二)

一、线程同步之 信号量使用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;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.