欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

QT Day06

程序员文章站 2022-03-14 10:14:24
...

1.QT多线程应用–在控制台打印消息
2.生产者和消费者实现(信号量)
3.生产者和消费者实例(条件等待)
4.获得网络地址信息
1 QT多线程应用-在控制台打印消息
1.1 问题
首先使用多线程在控制台同时打印消息。

1.2 方案
QThread类提供了一个平*立的方式来管理线程,在Qt应用程序中开启一个线程十分简单,只需继承Qthread并重写run()函数即可,run函数是线程的起点,在调用QThread::start(),新创建的线程调用这个函数启动线程运行。

本案例分别创建两个线程,继承于QThread,两个线程分别循环在终端打印1–9,在对话框界面创建3个按钮,“开始”按钮用于开始启动两个线程运行,“停止”按钮用于暂停两个线程运行,"退出“按钮用于退出程序。

1.3 步骤
实现此案例需要按照如下步骤进行。

步骤一:创建新工程

终端输入:qtcreator或点击桌面qt图标。启动Qt创造器。

$qtcreator
选择菜单"file/file or project",在"新建"对话框中依次选择"application"和"QT widgets Application",并点击"choose…"

在"项目介绍和位置"中指定项目的名称为:Ernie,并选择存储路径,Qt会在该路径下创建工程目录,点击"next"

在"kits"中选择"Desktop Qt 5.4.1 Gcc 64bit",前面打勾,点击"next"

在"Class information"中选择"QDialog"作为"Base class",并将"class name"设置为"ThreadDialog",勾选"Generate form",点击"next" ,点击"finsh"。

步骤二:使用设计师设计界面

双击ThreadDialog.ui,打开Qt设计师,设计界面如图-1所示:
QT Day06
步骤三:编写源程序头文件

双击ThreadDialog.h文件,编写头文件,代码如下:

#ifndef THREADDIALOG_H
#define THREADDIALOG_H
#include
#include <workThread.h>
#define MAXSIZE 5
namespace Ui {
class threadDialog;
}
class threadDialog : public QDialog
{
Q_OBJECT
public:
explicit threadDialog(QWidget *parent = 0);
~threadDialog();
private slots:
/在ui界面选中按钮组建,鼠标右键点击转到槽将自动生成
对应的槽函数,同时建立connect连接
/
void on_startButton_clicked();
void on_stopButton_clicked();
void on_quitButton_clicked();
private:
Ui::threadDialog *ui;
workThread *threadA;
workThread *threadB;
};
#endif // THREADDIALOG_H
在代码中声明了开始,停止,退出按键的槽函数,声明了两个workThread对象的指针,在侧边栏”Headers“中点击右键,选择”Add New“,添加一个C++ Headers File,文件名为 workThread.h文件,workThread类声明如下:

#ifndef WORKTHREAD_H
#define WORKTHREAD_H
#include
class workThread : public QThread
{
public:
workThread();
~workThread();
protected:
//重写run函数,多态
void run();
};
#endif // WORKTHREAD_H
步骤四:编写workThread.cpp源程序文件

在侧边栏”Sources“中点击右键,选择”Add New“,添加一个C++ Source File,文件名为 workThread.cpp文件,编写代码如下:

#include “workThread.h”
workThread::workThread(){
}
workThread::~workThread(){
}
//线程函数
void workThread::run(){
//获取线程句柄,内部标识
unsigned long threadId = (unsigned long)currentThreadId();
while(1){
for(int i=1; i<10 ;i++){
usleep(200000);
//qDebug("%lu:%d",threadId,i);
qDebug() << currentThreadId();
}
}
}
改写run函数,循环打印当前线程pid号

步骤五:编写源文件ThreadDialog.cpp

双击ThreadDialog.cpp文件,编写源文件,代码如下:

#include “threadDialog.h”
#include “ui_threadDialog.h”
threadDialog::threadDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::threadDialog)
{
ui->setupUi(this);
threadA = new workThread;
threadB = new workThread;
}
threadDialog::~threadDialog()
{
delete ui;
}
void threadDialog::on_startButton_clicked()
{
threadA->start();
threadB->start();
ui->startButton->setEnabled(false);
ui->stopButton->setEnabled(true);
}
void threadDialog::on_stopButton_clicked()
{
threadA->terminate();
threadA->wait();//回收线程资源
threadB->terminate();
threadB->wait();
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
}
void threadDialog::on_quitButton_clicked()
{
this->close();
}
点击开始按钮,通过threadA->start(); threadB->start();启动两个线程,并且使能停止按钮,失能开始按钮。

点击停止按钮,通过代码

threadA->terminate();
threadA->wait();//回收线程资源
threadB->terminate();
threadB->wait();
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);

暂停线程执行,并使能开始按钮,失能停止按钮

步骤六:编译运行

代码编辑完后,即可对工程进行编译,点击绿色按钮,即可编译运行,如果代码有错,会给出错误信息,运行结果如下图-2所示:
QT Day06
1.4 完整代码
本案例的头文件ThreadDialog.h完整代码如下所示:

#ifndef THREADDIALOG_H
#define THREADDIALOG_H
#include
#include <workThread.h>
#define MAXSIZE 5
namespace Ui {
class threadDialog;
}
class threadDialog : public QDialog
{
Q_OBJECT
public:
explicit threadDialog(QWidget *parent = 0);
~threadDialog();
private slots:
/在ui界面选中按钮组建,鼠标右键点击转到槽将自动生成
对应的槽函数,同时建立connect连接
/
void on_startButton_clicked();
void on_stopButton_clicked();
void on_quitButton_clicked();
private:
Ui::threadDialog *ui;
workThread *threadA;
workThread *threadB;
};
#endif // THREADDIALOG_H
本案例的头文件wordThread.h完整代码如下所示:

#include “workThread.h”
workThread::workThread(){
}
workThread::~workThread(){
}
//线程函数
void workThread::run(){
//获取线程句柄,内部标识
unsigned long threadId = (unsigned long)currentThreadId();
while(1){
for(int i=1; i<10 ;i++){
usleep(200000);
//qDebug("%lu:%d",threadId,i);
qDebug() << currentThreadId();
}
}
}
本案例的文件ThreadDialog.cpp完整代码如下所示:

#include “threadDialog.h”
#include “ui_threadDialog.h”
threadDialog::threadDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::threadDialog)
{
ui->setupUi(this);
threadA = new workThread;
threadB = new workThread;
}
threadDialog::~threadDialog()
{
delete ui;
}
void threadDialog::on_startButton_clicked()
{
threadA->start();
threadB->start();
ui->startButton->setEnabled(false);
ui->stopButton->setEnabled(true);
}
void threadDialog::on_stopButton_clicked()
{
threadA->terminate();
threadA->wait();//回收线程资源
threadB->terminate();
threadB->wait();
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
}
void threadDialog::on_quitButton_clicked()
{
this->close();
}
本案例的文件wordThread.cpp完整代码如下所示:

#include “workThread.h”
workThread::workThread(){
}
workThread::~workThread(){
}
//线程函数
void workThread::run(){
//获取线程句柄,内部标识
unsigned long threadId = (unsigned long)currentThreadId();
while(1){
for(int i=1; i<10 ;i++){
usleep(200000);
//qDebug("%lu:%d",threadId,i);
qDebug() << currentThreadId();
}
}
}
本案例的main.cpp完整代码如下所示:

代码
2 生产者和消费者实例(信号量)
2.1 问题
使用信号量解决生产者和消费者的同步问题。

2.2 方案
对于多线程的Qt应用程序,最基本的要求就是能够实现多个线程同时运行,而不会因为竞争共享中资源而引发同步问题,Qt提供了以下几种方式用于线程同步,本案例将使用QSemaphore 信号量实现生产者和消费者实例。QSemaphore 是QMutex的的泛化表现形式,它可以保护一定数量的相同资源,而一个mutex只保护一个资源QSemaphore支持两个基本的操作:

acquire(N) 获取资源,如果资源不够将阻塞,直到满足条件

release(N)释放资源N

使用available(),可以返回当前可用的信号量资源数量 。

2.3 步骤
实现此案例需要按照如下步骤进行。

步骤一:建立工作目录ThreadSemaphore,并进入ThreadSemaphore目录。

$mkdir ThreadSemaphore
$cd ThreadSemaphore
步骤二:编写源程序文件main.cpp

在终端输入编辑命令:

$vim main.cpp
代码如下:

#include
#include
#include
const qint32 DataSize = 10;
const qint32 BufferSize = 4;
//仓库:全局数据缓冲区
int buffer[BufferSize];
//控制生产者线程信号量:写入的数据缓冲区
QSemaphore freeSpace(BufferSize);
//控制消费者线程信号两:读取的数据缓冲区
QSemaphore usedSpace(0);
//生产者线程类
class threadProducer:public QThread
{
public:
void run(){
for(int i=0; i<DataSize; ++i){
//仓库有空闲的位置,生产一个数据
//空闲位置少一个,控制生产的信号量-1
freeSpace.acquire();
buffer[i] = i+1;
qDebug(“producer::%d”,buffer[i]);
//可消费的数据多一个,控制消费者信号量+1
usedSpace.release();
}
}
};
//消费者线程类
class threadConsumer:public QThread
{
public:
void run(){
for(int i=0; i<DataSize; ++i){
//仓库有可用的数据,消费一个数据
//可消费的数据少一个:控制消费者信号量-1
usedSpace.acquire();
qDebug(“consumer::%d”,buffer[i]);
//消费后,仓库空闲位置多一个:控制生产者信号量+1
freeSpace.release();
//usleep(200000);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
threadConsumer consumer;
threadProducer producer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return a.exec();
}
步骤三:编译运行。

在终端输入命令:

$qmake -project
$qmake
$make
$./ThreadSemaphore
运行结果如图-3所示:
QT Day06
2.4 完整代码
本案例完整代码如下所示:

代码
3 生产者和消费者实例(条件等待)
3.1 问题
使用条件等待解决生产者和消费者的同步问题。

3.2 方案
对于生产者和消费者的同步的问题,另一个解决方案是使用QWaitCondition和QMutex,QWaitCondition允许一个线程在满足一定条件下触发其它多个线程,这样可以比只使用互斥量提供更为精确的控制

在生产者和消费者的实例中,可以声明两个等待条件,即WaitCondition,一个互斥量和一个去变量,该变量用来存储缓冲区中有多少个“用过的”字节。

3.3 步骤
实现此案例需要按照如下步骤进行。

步骤一:建立工作目录ThreadWaitCondition,并进入ThreadWaitCondition目录。

$mkdir ThreadWaitCondition
$cd ThreadWaitCondition
步骤二:编写源程序文件main.cpp

在终端输入编辑命令:

$vim main.cpp
代码如下:

#include
#include
#include
#include
const qint32 DataSize = 10;
const qint32 BufferSize = 4;
//仓库:全局数据缓冲区
int buffer[BufferSize];
//条件:缓冲区不满则可以生产,否则等待消费
QWaitCondition bufferIsNotFull;
//条件:缓冲区不空则可以消费,否则等待生产
QWaitCondition bufferIsNotEmpty;
//满足条件访问全局缓冲区要加锁
QMutex mutex;
//记录缓冲区有多少个用过的字节
int usedSpace = 0;
//生产者线程类
class threadProducer:public QThread
{
public:
void run(){
for(int i=0; i<DataSize; ++i){
mutex.lock();
//如果仓库满了,不生产,而等待消费
while (usedSpace == BufferSize){
//wait函数先解锁,再阻塞当前线程
//消费者消费以后会唤醒当前线程,再加锁
bufferIsNotFull.wait(&mutex);
}
buffer[i] = i+1;
qDebug(“prodeucer::%d”,buffer[i]);
++usedSpace;
bufferIsNotEmpty.wakeAll();
mutex.unlock();
}
}
};
//消费者线程类
class threadConsumer:public QThread
{
public:
void run(){
for(int i=0; i<DataSize; ++i){
mutex.lock();
//如果仓库是空的,不消费,等待生产
while (usedSpace == 0){
//生产者生产以后会唤醒当前线程,再加锁
bufferIsNotEmpty.wait(&mutex);
}
qDebug(“consumer::%d”,buffer[i]);
–usedSpace;
bufferIsNotFull.wakeAll();
mutex.unlock();
}
}
};
步骤三:编译运行。

在终端输入命令:

$qmake -project
$qmake
$make
$./ThreadSemaphore
运行结果如图-4所示:
QT Day06
3.4 完整代码
本案例完整代码如下所示:

#include
#include
#include
#include
const qint32 DataSize = 10;
const qint32 BufferSize = 4;
//仓库:全局数据缓冲区
int buffer[BufferSize];
//条件:缓冲区不满则可以生产,否则等待消费
QWaitCondition bufferIsNotFull;
//条件:缓冲区不空则可以消费,否则等待生产
QWaitCondition bufferIsNotEmpty;
//满足条件访问全局缓冲区要加锁
QMutex mutex;
//记录缓冲区有多少个用过的字节
int usedSpace = 0;
//生产者线程类
class threadProducer:public QThread
{
public:
void run(){
for(int i=0; i<DataSize; ++i){
mutex.lock();
//如果仓库满了,不生产,而等待消费
while (usedSpace == BufferSize){
//wait函数先解锁,再阻塞当前线程
//消费者消费以后会唤醒当前线程,再加锁
bufferIsNotFull.wait(&mutex);
}
buffer[i] = i+1;
qDebug(“prodeucer::%d”,buffer[i]);
++usedSpace;
bufferIsNotEmpty.wakeAll();
mutex.unlock();
}
}
};
//消费者线程类
class threadConsumer:public QThread
{
public:
void run(){
for(int i=0; i<DataSize; ++i){
mutex.lock();
//如果仓库是空的,不消费,等待生产
while (usedSpace == 0){
//生产者生产以后会唤醒当前线程,再加锁
bufferIsNotEmpty.wait(&mutex);
}
qDebug(“consumer::%d”,buffer[i]);
–usedSpace;
bufferIsNotFull.wakeAll();
mutex.unlock();
}
}
};
4 获取网络地址信息
4.1 问题
首先通过网络接口类和相关函数获取当前主机名和对应IP地址,同时可显示网络地址信息。

4.2 方案
通过QHostInfo类获取当前主机名和IP,通过QNetworkInterface类获取网络接口的详细信息。

4.3 步骤
实现此案例需要按照如下步骤进行。

步骤一:创建新工程

终端输入:qtcreator或点击桌面qt图标。启动Qt创造器。

$qtcreator
选择菜单"file/file or project",在"新建"对话框中依次选择"application"和"QT widgets Application",并点击"choose…"

在"项目介绍和位置"中指定项目的名称为:NetInfo,并选择存储路径,Qt会在该路径下创建工程目录,点击"next"

在"kits"中选择"Desktop Qt 5.4.1 Gcc 64bit",前面打勾,点击"next"

在"Class information"中选择"QDialog"作为"Base class",并将"class name"设置为"NetInfoDialog",勾选"Generate form",点击"next" ,点击"finsh"。

步骤二:使用设计师设计界面

双击NetInfoDialog.ui,打开Qt设计师,设计界面如图-5所示:
QT Day06
步骤三:编写源程序头文件

双击NetInfoDialog.h文件,编写头文件,代码如下:

#ifndef NETINFODIALOG_H
#define NETINFODIALOG_H
#include
#include
#include
#include //显示详细信息
namespace Ui {
class NetInfoDialog;
}
class NetInfoDialog : public QDialog
{
Q_OBJECT
public:
explicit NetInfoDialog(QWidget *parent = 0);
~NetInfoDialog();
void getHostInfo(void);
private slots:
void on_detailButton_clicked();
private:
Ui::NetInfoDialog *ui;
};
#endif // NETINFODIALOG_H
步骤四:编写源文件NetInfoDialog.cpp

双击NetInfoDialog.cpp文件,编写源文件,代码如下:

#include “NetInfoDialog.h”
#include “ui_NetInfoDialog.h”
NetInfoDialog::NetInfoDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NetInfoDialog)
{
ui->setupUi(this);
getHostInfo();
}
NetInfoDialog::~NetInfoDialog()
{
delete ui;
}
void NetInfoDialog::getHostInfo(void)
{
//静态成员函数localHostName()获取当前主机名
QString hostName = QHostInfo::localHostName();
//显示主机名到ui
ui->hostnameEdit->setText(hostName);
//根据主机名获取主机地址
QHostInfo hostInfo = QHostInfo::fromName(hostName);
if(!hostInfo.addresses().isEmpty()){
//获取主机名对应的IP地址信息
QHostAddress address = hostInfo.addresses().first();
ui->ipAddrEdit->setText(address.toString());
}
}
void NetInfoDialog::on_detailButton_clicked()
{
QString detail="";
//获取当前主机所有的网络接口(网卡)列表
//QList类似数组
QList list=QNetworkInterface::allInterfaces();
//list.count:获取网络接口的数目
for(int i=0; i<list.count(); i++)
{
//list.at()类似取下表操作,从列表中取出具体网络接口
QNetworkInterface interface=list.at(i);
//获取设备名称
detail += tr(“设备:”)+interface.name()+"\n";
//获取mac地址
detail += tr(“硬件地址:”)+interface.hardwareAddress()+"\n";
//获取IPv4(IPv6)、广播、子网掩码
QList entryList=interface.addressEntries();
for(int j=0; j<entryList.count()-1; j++)
{
QNetworkAddressEntry entry=entryList.at(j);
detail += “\t” + tr(“IP 地址:”)+entry.ip().toString()+"\n";
detail += “\t” + tr(“子网掩码:”)+entry.netmask().toString()+"\n";
detail += “\t” + tr(“广播地址:”)+entry.broadcast().toString()+"\n";
}
}
//显示详细信息窗口,默认带一个ok按钮,点击关闭对话框
QMessageBox::information(this,tr(“详细信息”),detail);
}
步骤五:编译运行

代码编辑完后,即可对工程进行编译,点击绿色按钮,即可编译运行,如果代码有错,会给出错误信息,运行结果如下图-6,图-7所示:
QT Day06
4.4 完整代码
本案例的头文件完整代码如下所示:

#ifndef NETINFODIALOG_H
#define NETINFODIALOG_H
#include
#include
#include
#include //显示详细信息
namespace Ui {
class NetInfoDialog;
}
class NetInfoDialog : public QDialog
{
Q_OBJECT
public:
explicit NetInfoDialog(QWidget *parent = 0);
~NetInfoDialog();
void getHostInfo(void);
private slots:
void on_detailButton_clicked();
private:
Ui::NetInfoDialog *ui;
};
#endif // NETINFODIALOG_H
本案例的NetInfoDialog.cpp完整代码如下所示:

#include “NetInfoDialog.h”
#include “ui_NetInfoDialog.h”
NetInfoDialog::NetInfoDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::NetInfoDialog)
{
ui->setupUi(this);
getHostInfo();
}
NetInfoDialog::~NetInfoDialog()
{
delete ui;
}
void NetInfoDialog::getHostInfo(void)
{
//静态成员函数localHostName()获取当前主机名
QString hostName = QHostInfo::localHostName();
//显示主机名到ui
ui->hostnameEdit->setText(hostName);
//根据主机名获取主机地址
QHostInfo hostInfo = QHostInfo::fromName(hostName);
if(!hostInfo.addresses().isEmpty()){
//获取主机名对应的IP地址信息
QHostAddress address = hostInfo.addresses().first();
ui->ipAddrEdit->setText(address.toString());
}
}
void NetInfoDialog::on_detailButton_clicked()
{
QString detail="";
//获取当前主机所有的网络接口(网卡)列表
//QList类似数组
QList list=QNetworkInterface::allInterfaces();
//list.count:获取网络接口的数目
for(int i=0; i<list.count(); i++)
{
//list.at()类似取下表操作,从列表中取出具体网络接口
QNetworkInterface interface=list.at(i);
//获取设备名称
detail += tr(“设备:”)+interface.name()+"\n";
//获取mac地址
detail += tr(“硬件地址:”)+interface.hardwareAddress()+"\n";
//获取IPv4(IPv6)、广播、子网掩码
QList entryList=interface.addressEntries();
for(int j=0; j<entryList.count()-1; j++)
{
QNetworkAddressEntry entry=entryList.at(j);
detail += “\t” + tr(“IP 地址:”)+entry.ip().toString()+"\n";
detail += “\t” + tr(“子网掩码:”)+entry.netmask().toString()+"\n";
detail += “\t” + tr(“广播地址:”)+entry.broadcast().toString()+"\n";
}
}
//显示详细信息窗口,默认带一个ok按钮,点击关闭对话框
QMessageBox::information(this,tr(“详细信息”),detail);
}

相关标签: QT