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);
}相关文章:
c++ qt--线程(二)(第九部分)
c qt–线程(二)(第九部分) 一.线程并发 1.并发问题: 多个线程同时操作同一个资源(内存空间、文件句柄、网络句柄),可能会导致结果不一致的问题。发生的前提条件一定是多线程下…...
企业数据泄露不断,深信服EDR助企业构建数据“安全屋”
随着数字时代不断发展,数据泄露问题愈发严峻,个人信息安全面临着严重的威胁。近日,加拿大电信巨头加拿大贝尔(Bell Canada)对外披露了一起大规模数据泄露事件,该公司承认黑客入侵其系统,并窃取了190万个用户电子邮件地址以及约1700个用户姓名及活跃电话号码信息,相关损失无法估…...
单线复用iptv影响网速吗?
IPTV单线复用对网速有影响吗?这是一个比较常见的问题。如果你家的局域网是老的100M局域网LAN的路由器,走单线复用会影响你上网速度。但是如果你家的局域网是千兆网络,IPTV单线复用叠加上去的这点流量算不上什么,可以认为不占用网速…...
C语言中常用的字符串处理函数(strlen、strcpy、strcat、strcmp)
文章目录 写在前面1. strlen1.1 函数介绍1.2 模拟实现 2. strcpy2.1 函数介绍2.2 模拟实现 3. strcat3.1 函数介绍3.2 模拟实现 4. strcmp4.1 函数介绍4.2 模拟实现 写在前面 本篇文章介绍了C语言中常用的字符串处理函数,包括strlen、strcpy、strcat和strcmp。文章…...
Suricata – 入侵检测、预防和安全工具
一、Suricata介绍 Suricata是一个功能强大、用途广泛的开源威胁检测引擎,提供入侵检测 (IDS)、入侵防御 (IPS) 和网络安全监控功能。它执行深度数据包(网络流量)检查以及模式匹配,在威胁检测中非常强大。 工作流程: 主…...
vscode 乱码解决
windows 10 系统 vs code 编译运行和调试 C/C_vscode windows编译_雪的期许的博客-CSDN博客 VS Code默认文件编码时UTF-8,这对大多数情况是没有问题的,却偏偏对C/C有问题。如果以UTF-8编码保存C/C代码,那么只能输出英文,另外使用…...
SpringCloud(37):Spring Cloud Alibaba 综合集成架构演示
Spring Cloud是一个较为全面的微服务框架集,集成了如服务注册发现、配置中心、消息总线、负载均衡、断路器、API网关等功能实现。而在网上经常会发现Spring Cloud与阿里巴巴的Dubbo进行选择对比,这样做其实不是很妥当,前者是一套较为完整的架构方案,而Dubbo只是服务治理与R…...
【单片机】15-AD和DA转换
1.AD转换及其相关背景知识 1.基本概念 1.什么是AD转换? A(A,analog,模拟的,D,digital,数字的) 现实世界是模拟的,连续分布的,无法被分成有限份;…...
基于FPGA的I2C读写EEPROM
文章目录 前言一、I2C协议1.1 I2C协议简介1.2 物理层1.3 协议层 二、EEPROM2.1 型号及硬件规格2.2 各种读写时序 三、状态机设计四、项目源码:五、实现效果参考资料 前言 本次项目所用开发板FPGA芯片型号为:EP4CE6F17C8 EEPROM芯片型号为:24L…...
Viva Employee Communications Communities部署方案
目录 Viva Employee Communications & Communities产品介绍 1. 沟通中心(Communications Center) 2. 新闻和公告(News and Announcements)...
WPF向Avalonia迁移(三、项目结构)
前提: Avalonia版本11.0.0 1.配置文件 1.1 添加配置文件 1.2 读取配置文件 添加System.Configuration.ConfigurationManager using Avalonia.Controls; using System.Configuration;namespace AvaloniaApplication7.Views {public partial class MainWindow : W…...
cvpr24写作模板pdfLaTex编译器注意点小结
文章目录 1 更改作者显示 Anonymous CVPR submission2 \label标签3 换行符// 与换列符&4 \medskip5 首行缩进6 插入图片6.1 单幅图片6.2 并排显示\hfill Reference https://cvpr.thecvf.com/Conferences/2024 1 更改作者显示 Anonymous CVPR submission 这一行开头加上% …...
windows版php扩展包下载
php8有些扩展需自己下载,像redis 看下phpinfo中的PHP Extension Build,确定自己的php版本 windows.php.net - /downloads/pecl/releases/...
计算机竞赛 题目:基于深度学习的中文汉字识别 - 深度学习 卷积神经网络 机器视觉 OCR
文章目录 0 简介1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 简介 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的中文汉字识别 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! &a…...
Django跨域访问 nginx转发 开源浏览器
Django跨域访问 https://blog.csdn.net/lonelysnowman/article/details/128086205 nginx转发 https://blog.csdn.net/faye0412/article/details/75200607/ 开源浏览器 https://www.oschina.net/p/chromiumengine 浏览器油猴开发 https://blog.csdn.net/mukes/article/detail…...
Docker Alist 在线网盘部署
文章目录 拉取镜像创建并运行查看容器自动生成的密码在浏览器中进行访问 挂载本地磁盘 拉取镜像 docker pull xhofe/alist-aria2创建并运行 # -v /data/alist:/opt/alist/data 挂载本地目录 docker run -d --restartalways -v /data/alist:/opt/alist/data -p 5244:5244 -e P…...
Jmeter吞吐量控制器使用小结
吞吐量控制器(Throughput Controller)场景: 在同一个线程组里, 有10个并发, 7个做A业务, 3个做B业务,要模拟这种场景,可以通过吞吐量模拟器来实现.。 添加吞吐量控制器 用法1: Percent Executions 在一个线程组内分别建立两个吞吐量控制器, 分别放业务A和业务B 吞吐量控制器采…...
3分钟轻松实现网关网口连接罗克韦尔AB CompactLogix系列PLC
目录 EG网关网口连接罗克韦尔AB CompactLogix系列PLC 一. 准备工作 1.1 在对接前我们需准备如下物品 1.2 EG20网关准备工作 1.3 PLC准备工作 二. EMCP平台设置 2.1 新增EG设备 2.2 远程配置网关 2.3 网关绑定 2.4 通讯参数设置 2.5 创建设备驱动 2.5.1 添加变量 2.…...
vscode刷leetcode使用Cookie登录
1、打开vscode,选择扩展,搜索leetcode,选择第一个,带有中文力扣字样,安装后重启 2、选择这个小球,切换中文版本,切换后,会显示一个打勾 3、选择小球旁边的有箭头的小门࿰…...
每次启动Docker容器指定IP、hosts和端口
每次启动Docker容器指定IP、hosts和端口 1问题描述1解决办法1.1启动容器指定好IP、hostname、端口等信息1.2通过docker-compose启动容器,可以配置extra_hosts属性1.3通过k8s来管理容器,则在可以在创建pod的yaml文件通过hostAliases添加域名IP映射 2问题描…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
加密通信 + 行为分析:运营商行业安全防御体系重构
在数字经济蓬勃发展的时代,运营商作为信息通信网络的核心枢纽,承载着海量用户数据与关键业务传输,其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级,传统安全防护体系逐渐暴露出局限性&a…...
[特殊字符] 手撸 Redis 互斥锁那些坑
📖 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作,想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁,也顺便跟 Redisson 的 RLock 机制对比了下,记录一波,别踩我踩过…...
