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

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);//关闭句柄,使用计数-1pun1=nullptr;}}if(WaitForSingleObject(pun2,INFINITE/*一直等*/)==WAIT_OBJECT_0){//如果线程正常退出if(pun2){::CloseHandle(pun1);//关闭句柄,使用计数-1pun2=nullptr;}}if(WaitForSingleObject(pun3,INFINITE/*一直等*/)==WAIT_OBJECT_0){//如果线程正常退出if(pun3){::CloseHandle(pun3);//关闭句柄,使用计数-1pun1=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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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.hqwc.cn/news/130733.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis注解开发实现学生管理页面(分页pagehelper,多条件搜索,查看课程信息)

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 …

PowerShell pnpm : 无法加载文件 C:\Users\lenovo\AppData\Roaming\npm\pnpm.ps1

1、右键点击【开始】&#xff0c;打开Windows PowerShell&#xff08;管理员&#xff09; 2、运行命令set-ExecutionPolicy RemoteSigned 3、根据提示&#xff0c;输入A,回车 此时管理员权限已经可以运行pnpm 如果vsCode还报该错误 继续输入 4、右键点击【开始】&#xff0c;打…

澳大利亚教育部宣布ChatGPT将被允许在澳学校使用!

教育部长最近宣布&#xff0c;从 2024 年起&#xff0c;包括 ChatGPT 在内的人工智能将被允许在所有澳大利亚学校使用。 &#xff08;图片来源&#xff1a;卫报&#xff09; 而早些时候&#xff0c;澳洲各高校就已经在寻找与Chatgpt之间的平衡了。 之前&#xff0c;悉尼大学就…

Mac电脑交互式原型设计 Axure RP 8汉化最新 for mac

Axure RP 8是一款专业且快速的原型设计工具&#xff0c;主要用于定义需求、规格、设计功能和界面。这款工具主要适用于用户体验设计师、交互设计师、业务分析师、信息架构师、可用性专家和产品经理等职业。 Axure RP 8的主要特性包括能够快速设计出应用软件或Web网站的线框图、…

竞赛选题 深度学习 机器视觉 人脸识别系统 - opencv python

文章目录 0 前言1 机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别 2 深度学习-人脸识别过程人脸检测人脸识别Metric Larning 3 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 人脸识别系统 该项目…

XSS原理

原理&#xff1a; 这是一种将任意 Javascript 代码插入到其他Web用户页面里执行以达到攻击目的的漏洞。攻击者利用浏览器的动态展示数据功能&#xff0c;在HTML页面里嵌入恶意代码。当用户浏览改页时&#xff0c;这些潜入在HTML中的恶意代码会被执行&#xff0c;用户浏览器被攻…

消息队列 Kafka

Kafka Kafka 是一个分布式的基于发布/订阅模式的消息队列&#xff08;MQ&#xff0c;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域 为什么使用消息队列MQ 在高并发环境下&#xff0c;同步请求来不及处理会发生堵塞&#xff0c;从而触发too many conne…

【开题报告】如何借助chatgpt完成毕业论文开题报告

步骤 1&#xff1a;确定论文主题和研究问题 首先&#xff0c;你需要确定你的论文主题和研究问题。这可以是与软件开发、算法、人工智能等相关的任何主题。确保主题具有一定的研究性和可行性。 步骤 2&#xff1a;收集相关文献和资料 在开始撰写开题报告之前&#xff0c;收集相…

ACK 云原生 AI 套件:云原生 AI 工程化落地最优路径

作者&#xff1a;胡玉瑜(稚柳) 前言 在过去几年中&#xff0c;人工智能技术取得了突飞猛进的发展&#xff0c;涵盖了机器学习、深度学习和神经网络等关键技术的重大突破&#xff0c;这使得人工智能在各个领域都得到广泛应用&#xff0c;对各行各业产生了深远的影响。 特别值…

Virtual Box + Vagrant 快速搭建 Linux 虚拟开发环境

Virtual Box Vagrant 快速搭建 Linux 虚拟开发环境 1、根据自己所使用的操作系统平台&#xff0c;选择下载对应的虚拟机客户端软件 Virtual Box 并进行安装&#xff0c;这里选择的是 Virtual Box 7.0.10 Windows hosts 平台安装包。 选择安装目录为其他盘&#xff0c;避免默认…

360 G800行车记录仪,不使用降压线如何开机,8芯插头的定义。

G800记录仪的插头是这样的&#xff0c;图中标出了线的颜色。其中红色为常电V&#xff0c;黑色为GND负极&#xff0c;黄色为ACC受车是否启动控制。 这个记录仪原装的电源线没有降压功能&#xff0c;所以这里的V是12V。 记录仪内部有电源板&#xff0c;负责将12V降压为5V。 如果…

vue打印功能

安装 vue3-print-nb yarn add vue3-print-nb //或 npm install vue3-print-nbmain.js中引入 vue3-print-nb import { createApp } from vue; import App from ./App.vue; const app createApp(App); // 打印插件 import print from vue3-print-nb app.use(print) //页面内引…