c++ qt--线程(二)(第九部分)

news2024/10/7 18:27:08

c++ qt–线程(二)(第九部分)

一.线程并发

1.并发问题:

​ 多个线程同时操作同一个资源(内存空间、文件句柄、网络句柄),可能会导致结果不一致的问题。发生的前提条件一定是多线程下共享资源

2.写一个有并发问题的例子

1.创建一个控制台窗口

在这里插入图片描述

2.在main.cpp的mian函数中写下面代码
    //创建3个线程,都走相同线程函数中
    HANDLE pun1=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);
    HANDLE pun2=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);
    HANDLE pun3=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);

    if(WaitForSingleObject(pun1,INFINITE/*一直等*/)==WAIT_OBJECT_0){//如果线程正常退出
        if(pun1){
            ::CloseHandle(pun1);//关闭句柄,使用计数-1
            pun1=nullptr;
        }
    }

    if(WaitForSingleObject(pun2,INFINITE/*一直等*/)==WAIT_OBJECT_0){//如果线程正常退出
        if(pun2){
            ::CloseHandle(pun1);//关闭句柄,使用计数-1
            pun2=nullptr;
        }
    }

    if(WaitForSingleObject(pun3,INFINITE/*一直等*/)==WAIT_OBJECT_0){//如果线程正常退出
        if(pun3){
            ::CloseHandle(pun3);//关闭句柄,使用计数-1
            pun1=nullptr;
        }
    }

     qDebug()<<"-------------------count= "<<count;
3.在main.cpp中写下面代码
int count=0;//多个线程同时操作的变量
DWORD WINAPI fun (void *p){
    for(int i=0;i<100;i++){
        count++;
        
        qDebug()<<"count= "<<count;
        Sleep(50);
    }
 	    return 0;
}
4.分析结果

运行多次后,发现count变量最终的结果大多数不是300,而且输出count大多数会出现重复的值

这里是因为count++其实中间还分为几个阶段,当第一个线程存入值还没进行加操作,但是时间片结束了,这是就会到另一个线程,这里的count++是进行完整的,count存完并且加完了,然后再回到第一个线程,这时count进行加操作,这样就会出现count没加成功的情况,所以就会导致输出的count出现重复的值,count最终结果不是300

(三个线程一共执行了300次,正常count应该是300),这就是并发问题。

二.线程同步(解决并发问题)

1.线程同步的概念

线程同步就是通过协调线程执行的顺序,避免多个线程同时操作同一个资源导致并发问题,使多次执行结果一样

常见的线程同步方式:原子访问、关键段、时间、互斥量、条件变量、信号量

上面提到的并发问题,解决方法有很多,重点学习锁

2.原子访问

将上面代码进行修改

main.cpp的main函数中的代码不需要修改

对main.cpp中的其他需要修改的代码进行修改

int count=0;//多个线程同时操作的变量
DWORD WINAPI fun (void *p){
    for(int i=0;i<100;i++){
        ::InterlockedIncrement((long*)&count);//此函数使把变量按照原子操作的形式进行自增操作(++),参数要求是long*,这里进行一个强转,
        //::InterlockedDecrement((long*)&count);//此函数使把变量按照原子操作的形式进行自减操作(--),参数要求是long*,这里进行一个强转,
        qDebug()<<"count= "<<count;
        Sleep(50);
    }
}

3.关键段

1.概念:

将一块代码段锁住,只有当进入这块代码段的线程走完这块代码段,其他线程才能进入该块代码段

2.将main.cpp中的需要修改的代码进行修改(用关键段去使线程同步)
CRITICAL_SECTION cs;//定义一个关键段变量

DWORD WINAPI fun (void *p){
    for(int i=0;i<100;i++){
        
        ::EnterCriticalSection(&cs);//进入关键段,加锁
       	//实现的是这一段的代码锁住
        //------------
        count++;
        count2--;
     	//-----------
        ::LeaveCriticalSection(&cs);//离开关键段解锁
        qDebug()<<"count= "<<count;
        qDebug()<<"count2= "<<count2;
        Sleep(50);
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ::InitializeCriticalSection(&cs);//关键段的初始化
    
    //进行输出看结果是否符合预期(如果没有关键段可能会出现重复的数,这里会导致count最终结果不会到达300,count2最终结果不会到达-300)
    HANDLE pun1=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);
    HANDLE pun2=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);
    HANDLE pun3=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);

    if(WaitForSingleObject(pun1,INFINITE/*一直等*/)==WAIT_OBJECT_0){
        if(pun1){
            ::CloseHandle(pun1);
            pun1=nullptr;
        }
    }

    if(WaitForSingleObject(pun2,INFINITE/*一直等*/)==WAIT_OBJECT_0){
        if(pun2){
            ::CloseHandle(pun1);
            pun2=nullptr;
        }
    }

    if(WaitForSingleObject(pun3,INFINITE/*一直等*/)==WAIT_OBJECT_0){
        if(pun3){
            ::CloseHandle(pun3);
            pun1=nullptr;
        }
    }

    qDebug()<<"-------------------count= "<<count;
    qDebug()<<"-------------------count2= "<<count2;
    
    ::DeleteCriticalSection(&cs);//关键段的删除
    
    return a.exec();
}
3.将关键段进行封装(优化)
封装成一个类
class my{
private:
    CRITICAL_SECTION cs;//定义一个关键段变量

public:
    my(){::InitializeCriticalSection(&cs);}//构造函数中写关键段的初始化
    ~my(){::DeleteCriticalSection(&cs);}//析构函数写关键段的删除

    void Lock(){//此函数写进入关键段的函数
        ::EnterCriticalSection(&cs);//上锁
    }

    void UnLock(){//此函数写离开关键段的函数
        ::LeaveCriticalSection(&cs);//解锁
    }

} LOCK;//定义类对象


DWORD WINAPI fun (void *p){
    for(int i=0;i<100;i++){
        
        LOCK.Lock();//进入关键段,加锁
       	//实现的是这一段的代码锁住
        //------------
        count++;
        count2--;
     	//-----------
        LOCK.UnLock();//离开关键段解锁
        qDebug()<<"count= "<<count;
        qDebug()<<"count2= "<<count2;
        Sleep(50);
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    //进行输出看结果是否符合预期(如果没有关键段可能会出现重复的数,这里会导致count最终结果不会到达300,count2最终结果不会到达-300)
    HANDLE pun1=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);
    HANDLE pun2=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);
    HANDLE pun3=::CreateThread(nullptr,0,&fun,nullptr,0,nullptr);

    if(WaitForSingleObject(pun1,INFINITE/*一直等*/)==WAIT_OBJECT_0){
        if(pun1){
            ::CloseHandle(pun1);
            pun1=nullptr;
        }
    }

    if(WaitForSingleObject(pun2,INFINITE/*一直等*/)==WAIT_OBJECT_0){
        if(pun2){
            ::CloseHandle(pun1);
            pun2=nullptr;
        }
    }

    if(WaitForSingleObject(pun3,INFINITE/*一直等*/)==WAIT_OBJECT_0){
        if(pun3){
            ::CloseHandle(pun3);
            pun1=nullptr;
        }
    }

    qDebug()<<"-------------------count= "<<count;
    qDebug()<<"-------------------count2= "<<count2;
     
    return a.exec();
}
4.用关键段优化之前单例代码中存在的问题
1.之前单例代码如下
#include <iostream>
using namespace std;
/*
1.当前类最多只能创建一个实例
2.当前这个唯一的实例,必须由当前类创建(自主创建),而不是调用者创建
3.必须向整个系统提供全局的访问点,来获取唯一的实例


懒汉式:当第一次调用这个接口函数时,先创建单例					 时间换空间的做法
饿汉式:无论是否调用获取单例接口函数,都会提前创建单例				 空间换时间的做法

*/

class CSingleton {

	CSingleton(){}
	CSingleton(const CSingleton&) = delete;//弃用拷贝构造函数
	static CSingleton* m_spring;
	~CSingleton() {}
public:
	static struct DeleteSingleton {//保证申请的堆空间一定被回收
		~DeleteSingleton() {
			if(m_spring)
				delete m_spring;
			m_spring = nullptr;
		}
	} del;//静态对象,在程序结束时静态对象会自动被回收,然后就会调用析构函数,就一定能保证申请的堆空间被回收

	//有问题的代码,在多线程下,可能会创建多个对象
	static CSingleton* CreatCSingleton() {
		//加锁
		if (!m_spring) {//如果指针为空,则创建对象
			m_spring = new  CSingleton;
		}
		//解锁
		return m_spring;
	}

	static void DestoryCSingleton(CSingleton*& psin) {
		if (m_spring)
			delete m_spring;
		m_spring = nullptr;
		psin = nullptr;
	}
    
};
CSingleton::DeleteSingleton CSingleton::del;//类外定义初始化, 格式: 类型 类名::静态变量名
CSingleton* CSingleton:: m_spring(nullptr);

int main() {

	CSingleton* p1 = CSingleton::CreatCSingleton();
	CSingleton* p2 = CSingleton::CreatCSingleton();

	cout << p1 << "   " << p2 << endl;
	CSingleton::DestoryCSingleton(p1);
	return 0;
}
2.进行测试看是否会出现在多线程下,可能会创建多个对象的问题

将上面的代码修改为下面代码

#include <QCoreApplication>
#include <windows.h>
#include<QDebug>


//1.当前类最多只能创建一个实例
//2.当前这个唯一的实例,必须由当前类创建(自主创建),而不是调用者创建
//3.必须向整个系统提供全局的访问点,来获取唯一的实例
//
//
//懒汉式:当第一次调用这个接口函数时,先创建单例					 时间换空间的做法
//饿汉式:无论是否调用获取单例接口函数,都会提前创建单例				 空间换时间的做法


class CSingleton {

    CSingleton(){}
    CSingleton(const CSingleton&) = delete;//弃用拷贝构造函数
    static CSingleton* m_spring;
    ~CSingleton() {}
public:
    static struct DeleteSingleton {//保证申请的堆空间一定被回收
        ~DeleteSingleton() {
            if(m_spring)
                delete m_spring;
            m_spring = nullptr;
        }
    } del;//静态对象,在程序结束时静态对象会自动被回收,然后就会调用析构函数,就一定能保证申请的堆空间被回收

   
    static CSingleton* CreatCSingleton() {
     
        if (!m_spring) {//如果指针为空,则创建对象
            m_spring = new  CSingleton;
        }
       
        return m_spring;
    }

    static void DestoryCSingleton(CSingleton*& psin) {
        if (m_spring)
            delete m_spring;
        m_spring = nullptr;
        psin = nullptr;
    }


};
CSingleton::DeleteSingleton CSingleton::del;//类外定义初始化, 格式: 类型 类名::静态变量名
CSingleton* CSingleton:: m_spring(nullptr);

 //下面的5行代码是用来测试是否会出现问题的
DWORD WINAPI pun(void* p){
    Sleep(100);
    CSingleton *t=CSingleton::CreatCSingleton();//创建单例
    qDebug()<<t;//输出地址看有没有不同的地址出现,以此来确认是否出现了问题
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
	
    //下面的3行代码是用来测试是否会出现问题的
    for(int i=0;i<100;i++)
        ::CreateThread(nullptr,0,&pun,nullptr,0,nullptr);
    
     Sleep(5000);

    return a.exec();
}
3.用关键段进行优化
#include <QCoreApplication>
#include <windows.h>
#include<QDebug>


//1.当前类最多只能创建一个实例
//2.当前这个唯一的实例,必须由当前类创建(自主创建),而不是调用者创建
//3.必须向整个系统提供全局的访问点,来获取唯一的实例
//
//
//懒汉式:当第一次调用这个接口函数时,先创建单例					    时间换空间的做法
//饿汉式:无论是否调用获取单例接口函数,都会提前创建单例				 空间换时间的做法


class my{
private:
    CRITICAL_SECTION cs;

public:
    my(){::InitializeCriticalSection(&cs);}
    ~my(){::DeleteCriticalSection(&cs);}

    void Lock(){
        ::EnterCriticalSection(&cs);//上锁
    }

    void UnLock(){
        ::LeaveCriticalSection(&cs);//解锁
    }

} LOCK;

class CSingleton {

    CSingleton(){}
    CSingleton(const CSingleton&) = delete;//弃用拷贝构造函数
    static CSingleton* m_spring;
    ~CSingleton() {}
public:
    static struct DeleteSingleton {//保证申请的堆空间一定被回收
        ~DeleteSingleton() {
            if(m_spring)
                delete m_spring;
            m_spring = nullptr;
        }
    } del;//静态对象,在程序结束时静态对象会自动被回收,然后就会调用析构函数,就一定能保证申请的堆空间被回收

   
    static CSingleton* CreatCSingleton() {

        //加锁
        LOCK.Lock();
        if (!m_spring) {//如果指针为空,则创建对象
            m_spring = new  CSingleton;
        }
        //解锁
        LOCK.UnLock();
        return m_spring;
    }

    static void DestoryCSingleton(CSingleton*& psin) {
        if (m_spring)
            delete m_spring;
        m_spring = nullptr;
        psin = nullptr;
    }


};
CSingleton::DeleteSingleton CSingleton::del;//类外定义初始化, 格式: 类型 类名::静态变量名
CSingleton* CSingleton:: m_spring(nullptr);


DWORD WINAPI pun(void* p){

    Sleep(100);
    CSingleton *t=CSingleton::CreatCSingleton();
    qDebug()<<t;

}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    for(int i=0;i<100;i++){
        ::CreateThread(nullptr,0,&pun,nullptr,0,nullptr);
    }
   
    Sleep(5000);

    return a.exec();
}
4.上面代码还有能够进行优化的地方(49-59行的代码可以继续进行优化)
#include <QCoreApplication>
#include <windows.h>
#include<QDebug>


//1.当前类最多只能创建一个实例
//2.当前这个唯一的实例,必须由当前类创建(自主创建),而不是调用者创建
//3.必须向整个系统提供全局的访问点,来获取唯一的实例
//
//
//懒汉式:当第一次调用这个接口函数时,先创建单例					    时间换空间的做法
//饿汉式:无论是否调用获取单例接口函数,都会提前创建单例				 空间换时间的做法


int count1;
class my{
private:
    CRITICAL_SECTION cs;

public:
    my(){::InitializeCriticalSection(&cs);}
    ~my(){::DeleteCriticalSection(&cs);}

    void Lock(){
        ::EnterCriticalSection(&cs);//上锁
    }

    void UnLock(){
        ::LeaveCriticalSection(&cs);//解锁
    }

} LOCK;

class CSingleton {

    CSingleton(){}
    CSingleton(const CSingleton&) = delete;//弃用拷贝构造函数
    static CSingleton* m_spring;
    ~CSingleton() {}
public:
    static struct DeleteSingleton {//保证申请的堆空间一定被回收
        ~DeleteSingleton() {
            if(m_spring)
                delete m_spring;
            m_spring = nullptr;
        }
    } del;//静态对象,在程序结束时静态对象会自动被回收,然后就会调用析构函数,就一定能保证申请的堆空间被回收

   
    static CSingleton* CreatCSingleton() {
        if (!m_spring){//如果不是空的就直接不进入上锁和解锁的流程,这样可以省时间(如果两个线程一个很快一个很慢,那慢的那个一定是非空的)
            
            //加锁
            LOCK.Lock();
            ::InterlockedIncrement((long*)&count1);//这里用一个conut1变量来进行自增,看上锁和解锁的流程要进行多少次(如果没有此判断语句,那进入上锁和解锁的流程一共要100次,看能少进入上锁和解锁的流程多少次)
            
            if (!m_spring) {//如果指针为空,则创建对象
                m_spring = new  CSingleton;
            }
            //解锁
            LOCK.UnLock();
           
        }
        return m_spring;
    }

    static void DestoryCSingleton(CSingleton*& psin) {
        if (m_spring)
            delete m_spring;
        m_spring = nullptr;
        psin = nullptr;
    }


};
CSingleton::DeleteSingleton CSingleton::del;//类外定义初始化, 格式: 类型 类名::静态变量名
CSingleton* CSingleton:: m_spring(nullptr);


DWORD WINAPI pun(void* p){

    Sleep(100);
    CSingleton *t=CSingleton::CreatCSingleton();
    qDebug()<<t;

}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    for(int i=0;i<100;i++){
        ::CreateThread(nullptr,0,&pun,nullptr,0,nullptr);
    }
   
    Sleep(5000);
    qDebug()<<count1;//输出count1的值
    return a.exec();
}

三.用qt中的进度条组件和四个按钮组件实现线程的五大状态(用windowsAPI实现的)

1.创建一个窗口项目

在这里插入图片描述

2.添加所用的组件如下

在这里插入图片描述

这里还用到了栅格布局

3.将每个按键组件都用qt自带的创建槽函数功能创建槽函数

在这里插入图片描述

4.将每个按键的功能进行实现

1.mainwindow.h中的代码如下
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <windows.h>
#include "mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_3_clicked();

    void on_pushButton_clicked();

    void on_pushButton_4_clicked();

    void on_pushButton_2_clicked();
    
    void slots_setValue(int);//槽函数(用来对进度条进行操作的函数)
    
signals://信号(用来发送对进度条进行操作的信号)
    void signals_setValue(int);
    
private:
    Ui::MainWindow *ui;

public:
    HANDLE pun;//将线程变成类成员函数,使其可以在类的成员函数间进行使用
    bool m_isQuit;//看线程是否退出

public:
    Ui::MainWindow* getUI(){//公共接口,获取ui以对进度条组件创建的进度条对象进行修改
        return ui;
    }

public:
    mythread m_thread;
};
#endif // MAINWINDOW_H
2.mainwindow.cpp中的代码如下
#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_Handle=nullptr;

    connect(this,SIGNAL(signals_setValue(int)),this,SLOT(slots_setValue(int)),Qt::QueuedConnection);//两个线程之间信号和槽的绑定需要用队列连接,不能直连
}

MainWindow::~MainWindow()
{
    delete ui;
    //这里是防止没有进行结束操作,直接关闭了程序,导致线程没有正常结束,所以这里在程序结束时,看线程有没有被关闭,如果没有关闭那么关闭
    if(m_Handle){
        ::CloseHandle(m_Handle);
    }
    m_Handle=nullptr;
}



DWORD WINAPI fun(void *p){

    MainWindow* pun=( MainWindow*)p;
    int i=0;
    while(!pun->m_isQuit){//如果线程没有结束
        
        //pun->getUI()->progressBar->setValue(i);//这里发现此方法不好使(原因应该是因为有两个线程,信号和槽用了直连的方式,如果用队列连接的方式就不会有问题了,所以我们手动使用队列连接信号和槽,然后手动发射信号,用槽函数进行处理)
        
        //发射信号
        emit pun->signals_setValue(i);
        i=++i%101;
        Sleep(80);
    }

    return 0;
}


void MainWindow::on_pushButton_3_clicked()//开始
{
    if(!m_Handle){
        m_isQuit=false;
        m_Handle=CreateThread(nullptr,0,&fun,this,0,nullptr);
    }

}


void MainWindow::on_pushButton_clicked()//暂停
{
    ::SuspendThread(m_Handle);//挂起线程
}


void MainWindow::on_pushButton_4_clicked()//恢复
{
    ::ResumeThread(m_Handle);//恢复线程
}


void MainWindow::on_pushButton_2_clicked()//退出
{
    
    m_isQuit=true;
    if(WaitForSingleObject(m_Handle,INFINITE)==WAIT_OBJECT_0){//如果线程退出了
        if(m_Handle){
            ::CloseHandle(m_Handle);
            m_Handle=nullptr;
        }
    }

}

void MainWindow::slots_setValue(int i){//槽函数(用来对进度条进行操作的函数)
    ui->progressBar->setValue(i);
}


四.用qt中的进度条组件和四个按钮组件实现线程的五大状态(使用qt里自己封装的线程库实现的)

1.创建一个窗口项目

在这里插入图片描述

2.添加所用的组件如下

在这里插入图片描述

这里还用到了栅格布局

3.将每个按键组件都用qt自带的创建槽函数功能创建槽函数

在这里插入图片描述

4.将每个按键的功能进行实现

1.我们要使用qt里自己封装的线程库首先要新建一个类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.给创建的类添加父类(线程类的父类),添加线程库的头文件

在这里插入图片描述

3.将创建的类进行完善
1.类的头文件的代码如下
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include<windows.h>
class mythread : public QThread
{
    Q_OBJECT//如果用到了信号槽函数,那么需要这个宏
public:
    mythread();//构造函数
    ~mythread();//析构函数
public:
    virtual void run();//虚函数,线程运行的函数
    CRITICAL_SECTION m_cs;//创建关键段(此关键段是为了在暂停的时候,循环也会因为关键段暂停,使其线程真正的暂停)

public:
    bool m_isQuit;//用来判断线程函数是否退出的变量
    bool m_isPause;//用来判断线程函数是狗暂停的变量

signals://信号函数
    void signals_emitData(int);//用来发射信号的函数
};

#endif // MYTHREAD_H

2.类的源文件的代码如下
#include "mythread.h"
#include <QDebug>

mythread::mythread():m_isQuit(false),m_isPause(false)//初始化
{
    ::InitializeCriticalSection(&m_cs);//初始化关键段
}

mythread::~mythread()
{
    ::DeleteCriticalSection(&m_cs);//回收关键段
}

//qt 封装的线程库中的线程函数
void mythread::run(){//将此函数进行重写
    int i=0;
    while(!m_isQuit){//判断是否退出
        
        if(m_isPause){//判断是否暂停
             qDebug()<<"暂停";
              EnterCriticalSection(&m_cs);//进入关键段

              LeaveCriticalSection(&m_cs);//离开关键段
             continue;
        }

        //发射信号
        emit signals_emitData(i);
        i=++i%101;
        this->msleep(80);
    }
}
4.mainwindow.h中的代码如下(比用windowsAPI实现的mainwindow.h代码多了一个类对象)

此代码是在mainwindow.h的MainWindow的类中写的

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <windows.h>
#include "mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_3_clicked();

    void on_pushButton_clicked();

    void on_pushButton_4_clicked();

    void on_pushButton_2_clicked();

    void slots_setValue(int);

signals:
    void signals_setValue(int);

private:
    Ui::MainWindow *ui;

public:
    HANDLE m_Handle;
    bool m_isQuit;

public:
    Ui::MainWindow* getUI(){
        return ui;
    }
//下面这两行就是多的
public:
    mythread m_thread;
};
#endif // MAINWINDOW_H

5.mainwindow.cpp中的代码如下
#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_Handle=nullptr;

    connect(this,SIGNAL(signals_setValue(int)),this,SLOT(slots_setValue(int)),Qt::QueuedConnection);

    //线程类库中的信号 和 slots_setValue做绑定链接
        			 connect(&m_thread,SIGNAL(signals_emitData(int)),this,SLOT(slots_setValue(int)),Qt::QueuedConnection);
}

MainWindow::~MainWindow()
{
    delete ui;
    //这里是防止没有进行结束操作,直接关闭了程序,导致线程没有正常结束,所以这里在程序结束时,看线程有没有被关闭,如果没有关闭那么关闭
    if(m_Handle){
        ::CloseHandle(m_Handle);
    }
    m_Handle=nullptr;
}



DWORD WINAPI fun(void *p){

    MainWindow* pun=( MainWindow*)p;
    int i=0;
    while(!pun->m_isQuit){
        //发射信号
        emit pun->signals_setValue(i);
        i=++i%101;
        Sleep(80);
    }

    return 0;
}


void MainWindow::on_pushButton_3_clicked()//开始
{

    m_thread.m_isQuit=false;//为了下一次创建新线程能正常执行所做的操作(如果不进行此操作,那么线程刚创建就被回收了)
    
    m_thread.start(); //启动线程,执行线程函数 run

}


void MainWindow::on_pushButton_clicked()//暂停
{
    ::EnterCriticalSection(&m_thread.m_cs);//进入关键段(这里进入关键段之后,只有先离开关键段,才能再次进入关键段,以此来达到真正暂停的效果)
    
    m_thread.m_isPause=true;//暂停线程

}


void MainWindow::on_pushButton_4_clicked()//恢复
{
    m_thread.m_isPause=false;//恢复线程
    
    ::LeaveCriticalSection(&m_thread.m_cs);//离开关键段
}


void MainWindow::on_pushButton_2_clicked()//退出
{
    m_thread.m_isQuit=true;
}

void MainWindow::slots_setValue(int i){//槽函数(用来对进度条进行操作的函数)
    ui->progressBar->setValue(i);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1065855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现动态表单的一种思路 | 京东云技术团队

一、动态表单是什么 区别于传统表单前后端配合联调的开发实现方式&#xff0c;动态表单通过一种基于元数据管理的配置化方法来实现表单的动态生成&#xff0c;并能根据配置自由增改删指定字段。实现特定需求的自助化。 图1.1 传统表单前后台协作模式 图1.2 动态表单前后台协作…

Linux 逻辑卷

目录 一、认识 1、概念 2、术语&#xff1a; 1&#xff09;物理存储设备 2&#xff09;物理卷 3&#xff09;卷组 4&#xff09;PE物理区域 5&#xff09;逻辑卷 6&#xff09;LE逻辑区域 7&#xff09;VGDA卷组描述符区域 二、部署逻辑卷 1、物理卷管理 2、卷组…

搭建Windows上的Qt桌面开发环境

搭建Windows上的Qt桌面开发环境 准备有效邮箱安装VS2019 CommunityMicrosoft个人账号注册地址下载在线安装器安装C工具链 安装QtQt开发者账号注册地址下载在线安装器安装Qt 5.15工具链和Qt Creator 使用Qt Creator编译示例工程配置构建套件&#xff08;Kit&#xff09;打开示例…

特殊笔记_10/7

安装node到第4.1就行&#xff08;安装npm的淘宝镜像&#xff09; Node.js安装与配置&#xff08;详细步骤&#xff09;_nodejs安装及环境配置_LI4836的博客-CSDN博客 安装vscode 下载组件&#xff1a; 点击第五个 Auto Close Tag&#xff1a;自动闭合标签 Chinese (Simpli…

RabbitMQ集群搭建详细介绍以及解决搭建过程中的各种问题 + 配置镜像队列——实操型

RabbitMQ集群搭建详细介绍以及解决搭建过程中的各种问题 配置镜像队列——实操型 1. 准备工作1.1 安装RabbitMQ1.2 简单部署搭建设计1.3 参考官网 2. RabbitMQ 形成集群的方法3. 搭建RabbitMQ集群3.1 部署架构3.2 rabbitmq集群基础知识3.2.1 关于节点名称&#xff08;标识符&a…

2023年中国资产数字化监控运维管理系统行业分析:产品应用领域不断拓展[图]

资产监控运维管理是一门紧密结合生产实际的工程科学&#xff0c;是实现资产有效运营维护的重要手段。资产监控运维管理技术起源于美国和欧洲等国家和地区&#xff0c;经过几十年的理论研究和实际应用&#xff0c;资产监控运维管理技术为提高重大设备资产和系统的可靠性和安全性…

javaee ssm框架项目整合thymeleaf2.0 更多thymeleaf标签用法 项目结构图

创建ssmthymeleaf项目 创建ssmthymeleaf项目参考此文 thymeleaf更多常用标签 <!DOCTYPE html> <html lang"en" xmlns:th"http://www.thymeleaf.org"> <head><meta charset"UTF-8"><title>Title</title> …

练[WUSTCTF2020]朴实无华

[WUSTCTF2020]朴实无华 文章目录 [WUSTCTF2020]朴实无华掌握知识解题思路代码分析 关键paylaod 掌握知识 ​ 目录扫描&#xff0c;抓包放包&#xff0c;代码审计&#xff0c;php函数特性的了解&#xff1a;intval函数&#xff0c;md5特性绕过&#xff0c;RCE一些bypass方法 解…

新代仿真软件的使用方法

1、安装 必须解压到C盘根目录下面 2、按键功能说明 3、新代仿真密码 密码&#xff1a;520 4、繁体改简体 3209号参数&#xff1a;值改成119&#xff0c;重启后即可正常识别中文。 5、修改坐标 先进入【程序编辑】-【图形仿真】-按【F10】键-【仿真参数设定】 把绘图模式改…

泛微OA e-office平台uploadify.php任意文件上传漏洞

泛微OA e-office平台uploadify.php任意文件上传漏洞复现 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的…

横向AlGaN/GaN基SBD结构及物理模型数据库的开发

GaN基功率器件凭借其临界电场高、电子饱和漂移速度大、热导率高等优良性能在大功率快充、充电桩、新能源汽车等领域具备广泛应用空间。为进一步助推半导体高频、高功率微电子器件的发展进程&#xff0c;天津赛米卡尔科技有限公司技术团队依托先进的半导体TCAD仿真平台成功开发出…

给 Linux0.11 添加网络通信功能 (Day4: 完成 MIT6.S081 最终实验 网卡驱动(2. 启动 xv6 net 分支))

url: https://pdos.csail.mit.edu/6.S081/2020/labs/guidance.html lab guidance 介绍了调试技巧。 这种玩意儿可得好好看看啊&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 我们先把 xv6 跑起来吧&#xff0c;待会儿…

基于象群优化的BP神经网络(分类应用) - 附代码

基于象群优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于象群优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.象群优化BP神经网络3.1 BP神经网络参数设置3.2 象群算法应用 4.测试结果&#xff1a;5.M…

基于纵横交叉优化的BP神经网络(分类应用) - 附代码

基于纵横交叉优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于纵横交叉优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.纵横交叉优化BP神经网络3.1 BP神经网络参数设置3.2 纵横交叉算法应用 4.测试结果…

css记录写一个奇怪的按钮

完成作业的时候发现一个很有意思的按钮&#xff0c;记录一下记录一下 看看界面 可以看出是一个奇形怪状的按钮&#xff0c;而且在按下的时候&#xff0c;图片和文字的颜色会改变 尝试解决 <!DOCTYPE html> <html lang"zh"> <head><meta chars…

Zabbix监控系统 第一部分:zabbix服务部署+自定义监控项+自动发现与自动注册(附详细部署实例)

这里是目录 一、Zabbix概述1.1 简介1.2 zabbix组件1.2.1 zabbix server1.2.2 zabbix agent1.2.3 zabbix proxy1.2.4 zabbix get1.2.5 zabbix sender 1.3 工作原理1.4 端口号1.5 zabbix中预设的键值1.6 自定义监控项1.7 邮件报警的思路1.8 Zabbix自动发现和自动注册1.8.1 zabbix…

从 Greenplum 到 YMatrix,某头部动力电池厂商核心业务数据的迁移实践

前言 随着数字化浪潮的不断深入&#xff0c;近年来企业对于数据库的功能诉求不断多元化&#xff0c;同时数据量大幅增长&#xff0c;包括 Greenplum 在内的许多原有的数据库技术应对起来日渐捉襟见肘&#xff0c;一些大型企业替换和升级数据库的需求愈发迫切。 本文将为大家完…

五.docker+jenkins自动部署项目

一.敏捷开发相关概念 1.微服务的痛点 再来看一下我们的微服务架构 &#xff0c; 每个组件都需要服务器去部署&#xff0c;加起来可能需要几十个甚至上百个服务器。这样的微服务项目在部署上会遇到什么问题&#xff1f; 需要很多很多的服务器&#xff0c;服务器的采购安装&am…

picodet onnx转其它芯片支持格式时遇到

文章目录 报错信息解决方法两模型精度对比 报错信息 报错信息为&#xff1a; Upsample(resize) Resize_0 not support attribute coordinate_transformation_mode:half_pixel. 解决方法 整个模型转换过程是&#xff1a;paddle 动态模型转成静态&#xff0c;再用paddle2onnx…

open62541交叉编译

好久没有做嵌入式Arm Linux 的开发了。最近要将open62541 的应用程序移植到i.mx6u 嵌入式控制器。网络上讲解i.mx6 交叉编译的文章太多了。但是都过于复杂&#xff0c;大多数使用虚拟机实现。其实在ubuntu OS 下&#xff0c;开发ARM 嵌入式应用软件相对是相当简单的。这里记录了…