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问题描…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...