线程池:线程池的实现 | 日志
🌈C++专栏: 南桥谈C++
🌈C语言专栏: C语言学习系列
🌈Linux学习专栏: 南桥谈Linux
🌈数据结构学习专栏: 数据结构杂谈
🌈数据库学习专栏: 南桥谈MySQL
🌈Qt学习专栏: 南桥谈Qt
🌈菜鸡代码练习: 练习随想记录
🌈git学习: 南桥谈Git
文章目录
- 原理
- 线程池实现
- 日志
- 获取当前时间函数接口
- 启用类型设置
- 输出到屏幕
- 输出到文件
- 选择输出方式
- 创建日志消息
- 完整代码
- 携带日志的线程池设计
原理
在一个可执行程序内部存在多个线程和一个任务队列。如果任务队列里长时间没有任务,这些线程就会休眠,如果此时来了一个任务,那么线程就会被唤醒。像这种,提前创建好线程,需要的时候直接使用,我们称之为线程池。这种本质上就是一个生产消费模型。
线程池实现
//ThreadPool.hpp
#pragma once#include<iostream>
#include<unistd.h>
#include<string>
#include<vector>
#include<queue>
#include<functional>
#include"Thread.hpp"using namespace threadModel;static const int gdefaultnum=5;void test()
{while(true){std::cout<<"hello world"<<std::endl;sleep(1);}
}template<typename T>
class ThreadPool
{
private:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void Wakeup(){pthread_cond_signal(&_cond);}void WakeupAll(){pthread_cond_broadcast(&_cond);}void Sleep(){pthread_cond_wait(&_cond,&_mutex);}bool IsEmpty(){return _task_queue.empty();}void HandlerTask(const std::string& name) // this{while (true){LockQueue();//如果队列为空(有任务)while(IsEmpty()&&_isrunning) //线程没有任务,但是在工作,继续休眠{_sleep_thread_num++;Sleep();_sleep_thread_num--;}if(IsEmpty()&&!_isrunning) // 任务是空的,并且线程退出工作{std::cout<<name<<" quit..."<<std::endl;UnlockQueue();break;}// 队列不为空,有任务 或者 队列被唤醒// 取任务T t=_task_queue.front();_task_queue.pop();UnlockQueue();// 此处任务已经不在任务队列中,任务已经被拿走,处理任务和临界资源是两码事t(); // 处理任务,不能不用也不能在临界区中处理std::cout<<name<<": "<<t.result()<<std::endl;}}public:ThreadPool(int thread_num=gdefaultnum):_thread_num(thread_num),_isrunning(false) //刚开始线程没有使用,_sleep_thread_num(0){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_cond,nullptr);}void Init(){func_t func=std::bind(&ThreadPool::HandlerTask,this,std::placeholders::_1);for(int i=0;i<_thread_num;i++){std::string threadname="thread-"+std::to_string(i+1);_threads.emplace_back(threadname,func);}}void Start(){_isrunning=true;for(auto& thread:_threads){thread.Start();}}void Stop(){LockQueue();_isrunning=false;WakeupAll();UnlockQueue();}void Equeue(const T &in){LockQueue();if(_isrunning){_task_queue.push(in);// 如果当前有线程在等待,需要唤醒if(_sleep_thread_num>0){Wakeup();}}UnlockQueue();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}
private:int _thread_num;std::vector<Thread> _threads; // 管理多个线程std::queue<T> _task_queue; // 任务队列bool _isrunning; //当前线程是否在工作int _sleep_thread_num; //计数器:休眠的线程个数pthread_mutex_t _mutex;pthread_cond_t _cond;
};
日志
日志是软件运行的记录信息,可以向显示器打印,也可以向文件中打印,日志必须有特定的格式:
- [日志等级] [pid] [filename] [filenumber] [time] 日志内容(支持可变参数)
日志等级:DEBUG、INFO、WARNING、ERROR、FATAL(致命的)
// 日志等级
enum
{DEBUG=1,INFO,WARING,ERROR,FATAL
};
- 日志消息:日志等级、id、文件名、行号、当前时间等
// 日志消息
class logmessage
{public:std::string _level; // 日志等级pid_t _id; std::string _filename; // 文件名int _filenumber; // 行号std::string _cur_time;std::string _message_info;};
获取当前时间函数接口
std::string GetCurTime()
{time_t now=time(nullptr);struct tm* cur_time=localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d %02d:%02d:%02d",cur_time->tm_year+1900,cur_time->tm_mon+1,cur_time->tm_mday,cur_time->tm_hour,cur_time->tm_min,cur_time->tm_sec);return std::string(buffer);
}
time(nullptr)
返回当前的时间(从 1970 年 1 月 1 日到现在的秒数),并将其赋值给now
变量。time_t
是表示时间点的类型。localtime(&now)
将now
转换为当地时间,并返回一个指向tm
结构的指针。tm
结构包含了年、月、日、时、分、秒等信息。
启用类型设置
void Enable(int type)
{_type=type;
}
Enable
函数用于设置日志输出类型,可以选择输出到屏幕或文件。
输出到屏幕
void FlushLogToSCreen(const logmessage& lg)
{printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._cur_time.c_str(),lg._message_info.c_str());
}
FlushLogToSCreen
函数将日志信息格式化并输出到控制台。使用 printf
格式化字符串。
输出到文件
void FlushLogToFile(const logmessage& lg)
{std::ofstream out(_logfile,std::ios::app); // 追加打印if(!out.is_open())return;char logtxt[2048];snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._cur_time.c_str(),lg._message_info.c_str());out.write(logtxt,strlen(logtxt));out.close();
}
FlushLogToFile
函数将日志信息写入指定的文件。以追加模式打开文件,并在打开失败时返回。
使用snprintf
格式化日志信息,然后将其写入文件。
选择输出方式
void FlushLog(const logmessage& lg)
{switch(_type){case SCREEN_TYPE:FlushLogToSCreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}
}
创建日志消息
void logMessage(std::string filename,int filenumber,int level,const char* format,...)
{logmessage lg;lg._level=LevelToString(level);lg._id=getpid();lg._filename=filename;lg._filenumber=filenumber;lg._cur_time=GetCurTime();va_list ap;va_start(ap,format);char log_info[1024];vsnprintf(log_info,sizeof(log_info),format,ap);va_end(ap);lg._message_info=log_info;FlushLog(lg);
}
logMessage
函数用于创建一条新的日志消息。它接受文件名、文件编号、日志级别和格式化字符串作为参数。- 使用可变参数处理(
va_list
)来处理格式化字符串。 - 将生成的日志信息存储在
lg
对象中,并调用FlushLog
函数进行输出。 va_list ap;
声明了一个va_list
类型的变量ap
,它用于存储可变参数列表。在 C 语言中,va_list
是一个用于遍历不定数量参数的类型。va_start(ap, format);
:va_start
宏初始化ap
以指向函数参数列表中的第一个可变参数。这里的format
是最后一个固定参数,va_start
会从它的下一个参数开始读取可变参数。因此,ap
现在可以用来访问format
之后的所有参数。va_end(ap)
用于清理va_list
变量ap
。在读取完可变参数后,调用va_end
是良好的实践,它可以释放与ap
相关的资源(如果有的话)。
完整代码
#pragma once#include<iostream>
#include<string>
#include<cstring>
#include<sys/types.h>
#include<unistd.h>
#include<stdarg.h>
#include<ctime>
#include<fstream>
#include<pthread.h>
#include<cstdio>
#include"LockGuard.hpp"using std::cin;
using std::cout;
using std::endl;namespace log_ns
{// 日志等级enum{DEBUG=1,INFO,WARING,ERROR,FATAL};// 日志消息class logmessage{public:std::string _level; // 日志等级pid_t _id; std::string _filename; // 文件名int _filenumber; // 行号std::string _cur_time;std::string _message_info;};pthread_mutex_t glock=PTHREAD_MUTEX_INITIALIZER; // 定义一个全局的锁std::string LevelToString(int level){LockGuard lockguard(&glock);switch(level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARING:return "WARING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetCurTime(){time_t now=time(nullptr);struct tm* cur_time=localtime(&now);char buffer[128];snprintf(buffer,sizeof(buffer),"%d-%02d-%02d %02d:%02d:%02d",cur_time->tm_year+1900,cur_time->tm_mon+1,cur_time->tm_mday,cur_time->tm_hour,cur_time->tm_min,cur_time->tm_sec);return std::string(buffer);}#define SCREEN_TYPE 1#define FILE_TYPE 2const std::string glogfile="./log.txt";// 日志class Log{public:Log(const std::string& logfeile=glogfile):_logfile(logfeile),_type(SCREEN_TYPE){}void Enable(int type){_type=type;}void FlushLogToSCreen(const logmessage& lg){printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._cur_time.c_str(),lg._message_info.c_str());}void FlushLogToFile(const logmessage& lg){std::ofstream out(_logfile,std::ios::app); // 追加打印if(!out.is_open())return;char logtxt[2048];snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._cur_time.c_str(),lg._message_info.c_str());out.write(logtxt,strlen(logtxt));out.close();}void FlushLog(const logmessage& lg){switch(_type){case SCREEN_TYPE:FlushLogToSCreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void logMessage(std::string filename,int filenumber,int level,const char* format,...){logmessage lg;lg._level=LevelToString(level);lg._id=getpid();lg._filename=filename;lg._filenumber=filenumber;lg._cur_time=GetCurTime();va_list ap;va_start(ap,format);char log_info[1024];vsnprintf(log_info,sizeof(log_info),format,ap);va_end(ap);lg._message_info=log_info;//cout<<lg._message_info<<endl;// 打印日志FlushLog(lg);}~Log(){}private:int _type;std::string _logfile;};Log lg;#define LOG(Level,Format,...) do{lg.logMessage(__FILE__,__LINE__,Level,Format,##__VA_ARGS__);}while(0)#define EnableScreen() do{lg.Enable(SCREEN_TYPE);}while(0)#define EnableFile() do{lg.Enable(FILE_TYPE);}while(0)};
携带日志的线程池设计
//ThreadPool.hpp
#pragma once#include<iostream>
#include<unistd.h>
#include<string>
#include<vector>
#include<queue>
#include<functional>
#include"Thread.hpp"
#include"Log.hpp"using namespace threadModel;
using namespace log_ns;static const int gdefaultnum=5;void test()
{while(true){std::cout<<"hello world"<<std::endl;sleep(1);}
}template<typename T>
class ThreadPool
{
private:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void Wakeup(){pthread_cond_signal(&_cond);}void WakeupAll(){pthread_cond_broadcast(&_cond);}void Sleep(){pthread_cond_wait(&_cond,&_mutex);}bool IsEmpty(){return _task_queue.empty();}void HandlerTask(const std::string& name) // this{while (true){LockQueue();//如果队列为空(有任务)while(IsEmpty()&&_isrunning) //线程没有任务,但是在工作,继续休眠{_sleep_thread_num++;LOG(INFO,"%s thread sleep begin!\n",name.c_str());Sleep();LOG(INFO,"%s thread wakeup!\n",name.c_str());_sleep_thread_num--;}if(IsEmpty()&&!_isrunning) // 任务是空的,并且线程退出工作{UnlockQueue();LOG(INFO,"%s quit\n",name.c_str());break;}// 队列不为空,有任务 或者 队列被唤醒// 取任务T t=_task_queue.front();_task_queue.pop();UnlockQueue();// 此处任务已经不在任务队列中,任务已经被拿走,处理任务和临界资源是两码事t(); // 处理任务,不能不用也不能在临界区中处理LOG(DEBUG,"hander task done, task is: \n%s",t.result().c_str());}}public:ThreadPool(int thread_num=gdefaultnum):_thread_num(thread_num),_isrunning(false) //刚开始线程没有使用,_sleep_thread_num(0){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_cond,nullptr);}void Init(){func_t func=std::bind(&ThreadPool::HandlerTask,this,std::placeholders::_1);for(int i=0;i<_thread_num;i++){std::string threadname="thread-"+std::to_string(i+1);_threads.emplace_back(threadname,func);LOG(DEBUG,"construct thread %s done, init success.\n",threadname.c_str());}}void Start(){_isrunning=true;for(auto& thread:_threads){LOG(DEBUG,"Start thread %s done.\n",thread.Name().c_str());thread.Start();}}void Stop(){LockQueue();_isrunning=false;WakeupAll();UnlockQueue();LOG(INFO,"Thread Pool Stop Success!\n");}void Equeue(const T &in){LockQueue();if(_isrunning){_task_queue.push(in);// 如果当前有线程在等待,需要唤醒if(_sleep_thread_num>0){Wakeup();}}UnlockQueue();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}
private:int _thread_num;std::vector<Thread> _threads; // 管理多个线程std::queue<T> _task_queue; // 任务队列bool _isrunning; //当前线程是否在工作int _sleep_thread_num; //计数器:休眠的线程个数pthread_mutex_t _mutex;pthread_cond_t _cond;
};
//Main.cc
#include"ThreadPool.hpp"
#include"Task.hpp"
#include"Log.hpp"
#include<memory>using std::cin;
using std::cout;
using std::endl;
using namespace log_ns;int main()
{EnableScreen();//std::unique_ptr<ThreadPool> tp=std::make_unique<>(); //构建一个ThreadPool对象ThreadPool<Task> *tp=new ThreadPool<Task>();tp->Init();tp->Start();int cnt=10;while (cnt){// 不断地向线程池推送任务sleep(1);Task t(1,1);tp->Equeue(t);LOG(INFO,"equeue a task, %s\n",t.debug().c_str());sleep(1);}tp->Stop();LOG(INFO,"thread pool stop!\n");sleep(10);return 0;
}
// Thread.hpp
#pragma once
#include <pthread.h>
#include <iostream>
#include <string>
#include<functional>namespace threadModel
{// 线程执行的方法//typedef void (*func_t)(ThreadData* td);using func_t=std::function<void(const std::string&)>;class Thread{public:void Excute(){_isrunning = true;_func(_name);_isrunning = false;}public:Thread(const std::string &name, func_t func) : _name(name), _func(func){}// void *ThreadRoutine(void* args)实际上参数里面还有一个Thread* thisstatic void *ThreadRoutine(void *args) // 加上static后,参数里面就没有Thread* this{Thread *self = static_cast<Thread *>(args); // 获得当前对象self->Excute();return nullptr;}bool Start(){int n = ::pthread_create(&_tid, nullptr, ThreadRoutine, this);if (n != 0)return false;return true;}std::string Status(){if (_isrunning)return "running";elsereturn "sleep";}void Stop(){if (_isrunning){pthread_cancel(_tid);_isrunning = false;}}void Join(){pthread_join(_tid, nullptr);}std::string Name(){return _name;}~Thread(){Stop();}private:std::string _name;pthread_t _tid;bool _isrunning;func_t _func; // 线程执行的回调函数};
}
//Task.hpp
#pragma once
#include<iostream>
#include<string>
#include<functional>class Task
{public:Task(){}Task(int x,int y):_x(x),_y(y){}void Excute(){_result=_x+_y;}void operator()(){Excute();}std::string debug(){std::string msg=std::to_string(_x)+"+"+std::to_string(_y)+"=?";return msg;}std::string result(){std::string msg=std::to_string(_x)+"+"+std::to_string(_y)+"="+std::to_string(_result);return msg;}private:int _x;int _y;int _result;
};
相关文章:

线程池:线程池的实现 | 日志
🌈个人主页: 南桥几晴秋 🌈C专栏: 南桥谈C 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据…...
海信和TCL雷鸟智能电视的体验
买了型号为32E2F(9008)的海信智能的电视有一段时间了,要使用这个智能电视还真能考验你的智商。海信电视有很多优点,它的屏幕比较靓丽,色彩好看,遥控器不用对着屏幕就能操作。但也有不少缺点。 1. 海信智能电视会强迫自动更新操作…...

自动化学习3:日志记录及测试报告的生成--自动化框架搭建
一.日志记录 1.配置文件pytest.ini:将日志写入文件方便日后查询或查看执行信息。 需要将文件处理器(文件存放位置/时间/格式等等)添加到配置文件中的【日志记录器】 # pytest.ini [pytest] # ---------------日志文件,需要配合…...

【STM32单片机_(HAL库)】4-1【定时器TIM】定时器中断点灯实验
1.硬件 STM32单片机最小系统LED灯模块 2.软件 timer驱动文件添加定时器HAL驱动层文件添加GPIO常用函数定时器中断配置流程main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "timer.h"int main(void) {H…...
Linux编译安装Mysql笔记
1.Mysql介绍 MySQL是一个广泛使用的开源关系型数据库管理系统(RDBMS),它基于SQL(Structured Query Language)进行操作。MySQL是由瑞典MySQL AB公司开发的,后来被Sun Microsystems收购,最终成为…...

在java后端发送HTTPClient请求
简介 HttpClient遵循http协议的客户端编程工具包支持最新的http协议 部分依赖自动传递依赖了HttpClient的jar包 明明项目中没有引入 HttpClient 的Maven坐标,但是却可以直接使用HttpClient原因是:阿里云的sdk依赖中传递依赖了HttpClient的jar包 发送get请…...

【STM32单片机_(HAL库)】4-3-2【定时器TIM】测量按键按下时间1——编程实现捕获功能
测量按键按下时长思路 测量按键按下时间实验目的 使用定时器 2 通道 2 来捕获按键 (按键接PA0)按下时间,并通过串口打印。 计一个数的时间:1us,PSC71,ARR65535 下降沿捕获、输入通道 2 映射在 TI2 上、不分…...
MySQL:2059 - Authentication plugin ‘caching_sha2_password‘ cannot be loaded
关于MySQL 客户端在尝试连接到 MySQL 服务器时报错:“2059 - Authentication plugin caching_sha2_password cannot be loaded”,具体是由于 MySQL 服务器默认使用的 caching_sha2_password 认证插件无法加载或不被当前客户端支持。 错误原因 MySQL 8.0…...

【JavaSE】反射、枚举、lambda表达式
目录 反射反射相关类获取类中属性相关方法常用获得类相关的方法示例常用获得类中属性相关的方法示例获得类中注解相关的方法 反射优缺点 枚举常用方法优缺点 枚举与反射lambda表达式语法函数式接口简化规则使用示例变量捕获集合中的应用优缺点 反射 Java的反射(refl…...
P3227 [HNOI2013] 切糕
题意: n ∗ m n*m n∗m的矩阵,每个点可以选择一个值 a i , j k a_{i,j}k ai,jk,然后你能获得 w ( i , j , k ) w(i,j,k) w(i,j,k)的得分,但是相邻两点之间的差值有限制,让你求最大得分。 考虑最小割。 每个点 ( i , j ) (i,j) (i,j)弄出一条长为 R…...

超分服务的分量保存
分量说明 分量的概念主要是对于显卡解码,编码和网络传输而言,显卡可以同时进行几个线程,多个显卡可以分布式计算,对分量进行AI识别,比如我们有cuda的显卡,cuda的核心量可以分给不同的分片视频,第…...

Windows11系统下SkyWalking环境搭建教程
目录 前言SkyWalking简介SkyWalking下载Agent监控实现启动配置SkyWalking启动Java应用程序启动Elasticsearch安装总结 前言 本文为博主在项目环境搭建时记录的SkyWalking安装流程,希望对大家能够有所帮助,不足之处欢迎批评指正🤝ᾑ…...
前端BOM常用操作
BOM操作常用命令详解及代码案例 BOM(Browser Object Model)是浏览器对象模型,是浏览器提供的JavaScript操作浏览器的API。BOM提供了与网页无关的浏览器的功能对象,虽然没有正式的标准,但现代浏览器已经几乎实现了Java…...
【Go】-viper库的使用
目录 viper简介 viper使用 通过viper.Set设置值 读取配置文件说明 读取配置文件 读取多个配置文件 读取配置项的值 读取命令行的值 io.Reader中读取值 写配置文件 WriteConfig() 和 SafeWriteConfig() 区别: viper简介 配置管理解析库,是由大神 Steve Fr…...

JavaWeb酒店管理系统(详细版)
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

C++ | 定长内存池 | 对象池
文章目录 C | 定长内存池 | 对象池一、内存池的引入二、代码中的内存池实现 - ObjectPool类(一)整体结构(二)内存分配 - New函数(三)内存回收 - Delete函数 三、内存池在TreeNode示例中的性能测试演示四、脱…...

python画图|自制渐变柱状图
在前述学习过程中,我们已经通过官网学习了如何绘制渐变的柱状图及其背景。 掌握一门技能的最佳检验方式就是通过实战,因此,本文尝试做一些渐变设计。 前述学习记录可查看链接: Python画图|渐变背景-CSDN博客 【1】柱状图渐变 …...

基于RPA+BERT的文档辅助“悦读”系统 | OPENAIGC开发者大赛高校组AI创作力奖
在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…...

K8S部署流程
一、war打包镜像(survey,analytics,trac系统) 代码打包成war准备tomcat的server.xml文件,修改connector中8080端口为项目的端口 修改前: <Connector port"8080" protocol"HTTP/1.1"connectionTimeout"20000"redirect…...

DevExpress WinForms中文教程:Data Grid - 如何添加或删除行?
本教程介绍DevExpress WinForm的Data Grid控件UI元素和API,它们使您和最终用户能够添加或删除数据行。您将首选学习如何启用内置的数据导航器,然后学习如何使用Microsoft Outlook启发的New Item行添加新记录。最后教程将向您展示基本的API,它…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...