对于线程过去写的比较少,现在也几乎是小白的水平。先创建Qt empty project,即Qt空项目,打开QtCreate->文件->新建文件或项目->其它项目->empty qmke project,接着按提示创建Qt空项目,创建之后,在项目上右键添加新文件,选择源文件,创建main.cpp文件,添加如下代码:
main.cpp
#include <QtCore>int main(int argc,char* argv[])
{QCoreApplication app(argc,argv);return app.exec();
}
再添加新文件,选择类,基类为QThread,类名为myThread,最后添加代码后文件内容为:
myThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QThread>class myThread : public QThread
{Q_OBJECT
public:explicit myThread(QObject *parent = nullptr);
protected:void run();
private:
// volatile bool stopped;
};#endif // MYTHREAD_H
myThread.cpp
#include "mythread.h"
#include "Counter.h"
#include <QDebug>myThread::myThread(QObject *parent):QThread(parent)
{
// stopped = false;
}void myThread::run()
{Counter counter;quint8 i = 4;while(i--){counter.increment();
// int n = counter.GetValue();
// qDebug()<<QString("n:").arg(counter.GetValue());qDebug()<<"n:"<<counter.GetValue();}
}
再添加另一个线程类,同样在项目上右键添加新文件,选择类,类名mythreadread,基类为QThread,创建好这个类之后,添加完代码之后,文件内容如下:
mythreadread.h
#ifndef MYTHREADREAD_H
#define MYTHREADREAD_H#include <QThread>class mythreadread : public QThread
{Q_OBJECT
public:explicit mythreadread(QObject *parent = nullptr);
protected:void run();
};#endif // MYTHREADREAD_H
mythreadread.cpp
#include "mythreadread.h"
#include "Counter.h"
#include <QDebug>mythreadread::mythreadread(QObject *parent):QThread(parent)
{}void mythreadread::run()
{Counter counter;quint8 i = 4;while(i--){counter.decrement();qDebug()<<"n="<<counter.GetValue();}
}
以上两个线程类创建完成之后,在项目上右键添加新文件Counter.h,即添加头文件,添加之后,修改其代码如下:
Counter.h
#ifndef COUNTER_H
#define COUNTER_Hclass Counter
{
public:Counter():n(0){}void increment(){n++;}void decrement(){n--;}int GetValue()const{return n;}//该函数不能修改类成员变量的值
private:int n;
};
#endif // COUNTER_H
现在在main函数中修改代码如下:
main.cpp
#include <QtCore>
#include "mythread.h"
#include "mythreadread.h"myThread thread1;
mythreadread thread2;int main(int argc,char* argv[])
{QCoreApplication app(argc,argv);//针对各自线程中的特定对象的成员操作,互不影响thread1.start();thread1.wait();thread2.start();thread2.wait();return app.exec();
}
运行程序,结果如下:
n: 1
n: 2
n: 3
n: 4
n= -1
n= -2
n= -3
n= -4
结论:第一个线程启动后,在其线程函数中创建了Counter的类对象,并循环调用四次Counter类的加计数函数,然后输出每一次加计数后的值,最后调用wait()函数等待线程的结束,第一个线程结束之后,启动第二个线程,在该线程中对Counter类的成员变量减计数,并输出每一次减计数后的值,最后等带线程二结束之后,主程序关闭。其中有三个点需要注意:
- 在Counter类的定义中,将函数的声明与定义放在了同一个文件Counter.h中。需要注意的是,在同一个文件中定义并声明一个类时,函数的定义不能在该类外,否则会出现错误提示,重定义该文件。像下面所写一样:
#ifndef COUNTER_H
#define COUNTER_Hclass Counter
{
public:Counter():n(0){}int GetValue();
private:int n;
};
void Counter::GetValue()
{return n;
}
#endif // COUNTER_H
这样在类中定义成员函数,会导致编译错误,只能在类中进行定义。
#ifndef COUNTER_H
#define COUNTER_Hclass Counter
{
public:Counter():n(0){}int GetValue(){return n;}
private:int n;
};#endif // COUNTER_H
- 引入可重入的概念,一个类可以被多个线程使用,但每一次只能是调用该类自己的成员函数或成员变量。上面两个线程,在run()函数中分别对Counter类的成员函数进行操作,针对的是每一个Counter类自己的成员函数的操作,所以两个线程之间没有涉及到数据共享,线程同步等问题,可以线程实际是独立的。
- 如果将main.cpp中主函数中的两个线程的启动顺序变为如下:
thread1.start();thread2.start();thread1.wait(); thread2.wait();
那么程序的运行结果是怎么样呢?其运行结果如下:
n: 1
n= -1
n: 2
n= -2
n: 3
n= -3
n: 4
n= -4
可以看出,两个线程几乎是同时运行的,他们之间并行运行各自的run()函数,在对Counter类操作时,n的初始值都是从0开始,互不影响。
附加总结:
创建线程的方法之一是创建一个继承自QThread的类,引入#include头文件,在类的声明的开头加入Q_OBJECT宏,在类中重写run()函数,在类定义中对run()函数进行定义,即线程需要做的事情在run()函数中编写,线程类便创建完成了。线程调用时引入线程所在类的头文件,在需要线程的地方,定义线程对象,并调用start()函数启动线程,线程结束的地方可以调用stop()函数停止线程,但在之前最好加上wait()函数等待线程的结束。
terminate的说明
terminate并不会立即终止线程,它取决于系统的调度,此外调用这个函数时最好在其后调用一下wait()函数等待线程结束,但是terminate()存在危险性,因为无法知道它在何时会关闭线程,而且关闭线程时不会做清理工作,尽量少用。