c/c++开发,无可避免的模板编程实践(篇八)

news2024/11/28 18:50:10

一、借用标准库模板构造自己的模板

        通常,模板设计是遵循当对象的类型不影响类中函数的行为时就使用模板。这也就是为何标准库提供大部分的模板都是与容器、迭代器、适配器、通用算法等有关,因为这些主要是除了对象集合行为,如读写、增删、遍历、排序、比较等大多与对象类型无关的逻辑行为。

        在实际项目中,本人建议大家在使用模板时,采用的最好模板设计方式是走适配器方法,就像标准库基于vector、list、deque这些基准容器,通过适配封装去实现stack、queue、priority queue这些容器适配器一样。我们根据自己项目需要,采用标准库提供的容器及算法,创建自己的类模板应用到项目中。

二、基于标准库模板创建自定义类模板案例

        2.1 自定义容器适配器

        在本文中,将设计一个日志消息队列的容器适配器,基于双端队列(deque)容器和互斥锁,通过适配,创建一个线程安全的消息队列类模板。该消息队列类通过二次封装,实现数据末端添加数据,头部取出数据,队列大小有限制的跨线程安全消息缓冲区。

        首先创建一个queuedata.h和queuedata.cpp源文件,在queuedata.h文件实现消息队列类模板进行声明,在queuedata.cpp源文件进行定义,在queuedata.h文件末通过#include "queuedata.cpp"引用定义内容。本文中,QueueData类模板虽然说类似容器适配器,但是没有提供配套的迭代器iterator,大家感兴趣的可以自行实现一下(就是将deque容器的迭代器iterator做一下中转即可)。

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _QUEUE_DATA_H_
#define _QUEUE_DATA_H_

/***********************************************************************
  *Copyright 2023-02-06, pyfree
  *
  *File Name       : queuedata.h
  *File Mark       : 
  *Summary         : 
  *数据队列类模板,线程安全,对std::deque进行适配,通过PYMutex锁约束线程安全,提供数据增删等功能接口
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#include <deque>
#include <queue>
#include <stdio.h>
#include <string.h>
#include <string>

#include "Mutex.h"

template <class T>
class QueueData
{
public:
	QueueData(std::string desc = "thread_queue");
	~QueueData();
	//
	/**
	 * 获取队列大小
	 * @return {int } 队列大小
	 */
	int size();
	/**
	 * 判定队列是否为空
	 * @return {bool } 是否为空队列
	 */
	bool isEmpty();
	/**
	 * 获取队列头元素
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool getFirst(T &it);
	/**
	 * 删除元素
	 * @return {bool } 是否成功
	 */
	bool removeFirst();
	/**
	 * 获取队列头元素,并从队列终删除
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool pop(T &it);
	/**
	 * 从队列头开始逐步获取多个元素,并剔除
	 * @param its {queue<T>&} 获取到的元素集
	 * @param sizel {int} 一次获取多少个
	 * @return {bool } 至少获取一个元素以上则成功
	 */
	bool getList(std::queue<T> &its,unsigned int sizel=5);
	/**
	 * 从队列尾部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add(T it);
	/**
	 * 从队列头部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add_front(T it);
	/**
	 * 清空元素
	 * @return {void }
	 */
	void clear();
private:
	void init();
	QueueData& operator=(const QueueData&) {return this;};
protected:
	std::string queue_desc;
private:
	/点集转发
	
	//协议解析结果缓存
	std::deque<T> datacache_queue;	//队列容器
	PYMutex m_Mutex;				//线程锁,或者如果更彻底采用acl库,采用acl::thread_mutex替代
	//
	static unsigned int QSize;		//队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
	//
	int queue_overS;				//队列溢出次数计数
};

#include "queuedata.cpp"

#endif //_QUEUE_DATA_H_

       2.2 非类型模板参数的结构体模板

         传入消息队列类的信息是一个非类型模板参数的结构体模板,可以根据实际项目需要选择消息缓存区最大字节数。创建LogDef.h源文件,实现消息结构体模板,并定义日志等级枚举值。该结构体在定义对象时可以采用默认的方法MyLogStruct<> obj,也可以指定大小MyLogStruct<DATA_SIZE_MAX_LEVLE2> obj,或指定任意值作为缓存区域大小,MyLogStruct<100> obj。

#ifndef _LOG_DEF_H_
#define _LOG_DEF_H_
/***********************************************************************
  *Copyright 2023-02-06, pyfree
  *
  *File Name       : LogDef.h
  *File Mark       : 
  *Summary         : Log线程类使用到的日志等级枚举类型,日志信息格式-用于日志缓冲区存放日志的结构体
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#ifdef __linux__
#include <string.h>
#endif 

typedef enum eLogType 
{
	eLog_DEBUG			= 1,
	eLog_TRACE			= 2,
	eLog_NOTICE			= 3,
	eLog_WARNING 		= 4,
	eLog_ERROR			= 5
}LogLevel;

#define DATA_SIZE_MAX_LEVLE1 	1024	//日志缓存信息空间大小(1024字节)
#define DATA_SIZE_MAX_LEVLE2 	512		//日志缓存信息空间大小(512字节)
#define DATA_SIZE_MAX_LEVLE3 	256		//日志缓存信息空间大小(256字节)

//日志缓存信息结构体,采用结构体默认赋值
template<int SIZE = DATA_SIZE_MAX_LEVLE3>
struct MyLogStruct
{
	MyLogStruct():type(0)
	{
		memset(szBuffer, 0, SIZE);
	};
	int 	type;
	char	szBuffer[SIZE];
};

#endif //_LOG_DEF_H_

        2.3 日志记录类设计

        下来就是日志记录功能部分,创一个日志类,该类有一个QueueData<MyLogStruct<> > logs_cache对象用来缓存日志。该日志类还是一个单体模式设计,并继承一个线程类。线程循环体内,日志对象会不断去读取缓存区内的日志信息,如果读取到信息,就写入到日志文件内。另外,为了方式日志记录过大造成磁盘存储空间不足,将有一个DiskSpaceMgr类对象来定期巡检磁盘剩余空间,在存储空间不足时,去删除旧的日志信息。

        创建Log.h和Log.cpp源文件,Log.h源文件如下:

#ifndef _CLOGGER_H_
#define _CLOGGER_H_
/***********************************************************************
  *Copyright 2023-02-06, pyfree
  *
  *File Name       : Log.h
  *File Mark       : 
  *Summary         : Log写入独立线程类,提供添加日志信息进入缓存接口,自身不断从缓存读取日志信息写入文件
  *					会启动一个磁盘空间管理线程对象DiskSpaceMgr,在磁盘空间不足时,删除旧日志
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#include "myThread.h"
#include "LogDef.h"
#include "queuedata.h"

class DiskSpaceMgr;

class CLogger : public MyThread
{
public:
	void Log(const eLogType type, const char* lpszFormat, ...);
	static CLogger* createInstance( void );
private:
	CLogger();
	~CLogger();
	int Run();
	
	void WriteLog( const int iMsgType, const char * strMsg);
	void WriteLogToFile( std::string strPath,std::string strTime,const char * strMsg);
	std::string createFileName(std::string strTime_);
	std::string strTime();
	std::string createLogTimeAndTypeDesc(const unsigned int iMsgType, std::string strTime_);
private:
	static CLogger* m_pLogInstance;
	bool running;
	FILE * fpLog;
	std::string cur_log_path;
	//for cache
	QueueData<MyLogStruct<> > logs_cache;
	//
	DiskSpaceMgr *disk_space_mgr;
};
#endif //_CLOGGER_H_

       2.4 磁盘剩余空间管理类

         对象DiskSpaceMgr *disk_space_mgr是由Logger类创建的,该磁盘空间管理类是继承了类线程类的,在线程循环体内,不间断地巡检指定磁盘的剩余空间是否小于限定空间大小,小于时,将按配置要求去删除旧的日志信息。

        创建spaceMgr.h和spaceMgr.cpp源文件,spaceMgr.h内容如下:

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _SPACE_MGR_H_
#define _SPACE_MGR_H_
/***********************************************************************
  *Copyright 2023-02-06, pyfree
  *
  *File Name       : spaceMgr.h
  *File Mark       : 
  *Summary         : 磁盘空间巡检线程类,根据预留空间要求删除app日志文件
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#ifdef linux
#include <string>
#endif
#include <vector>

#include "myThread.h"

class DiskSpaceMgr : public MyThread
{
public:
	/**
	 * 构造函数,如传入参数D 2000 log log,则指明当D盘空闲空间不足2000M时,删除当前目录/log下的log(后缀)文件
	 * @param disk_ {char} 指定盘符
	 * @param fsl_ {int} 预留空间大小,单位MB
	 * @param dir_ {string} 指定目录
	 * @param ext_ {string} 扩展名
	 * @return { } 
	 */
	DiskSpaceMgr(char disk_, int fsl_,std::string dir_="log",std::string ext_="log");
	virtual ~DiskSpaceMgr(void);
	int Run();

	void add(std::string dir_,std::string ext_);
private:
	struct DelInfo
	{
		DelInfo(std::string dir_,std::string ext_)
			: dir(dir_), ext(ext_)
		{};
		std::string dir;
		std::string ext;
	};
private:
	//用于日志删除
	std::vector<DelInfo> dirs;  //存储目录
	char DiskStr;         //日志存储磁盘
	int freeSizeLimit;    //磁盘预留空间要求
};

#endif

        2.5 其他辅助类及函数设计

        日志类及磁盘管理类都继承了线程类MyThread,是通过myThread.h和myThread.cpp声明定义的,myThread.h内容如下:

#ifndef _MYTHREAD_H
#define _MYTHREAD_H
/***********************************************************************
  *Copyright 2023-02-06, pyfree
  *
  *File Name       : myThread.h
  *File Mark       : 
  *Summary         : 
  *支持windows和linux的线程类,提供基本的线程功能(循环、启动、退出)
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#ifdef WIN32
#include <process.h>
#include <iostream>

typedef void* HANDLE_THREAD;
#else
#include <pthread.h>
#include <unistd.h>

typedef pthread_t HANDLE_THREAD;
#endif

class MyThread
{
public://func
	MyThread();                     // constructed function
	~MyThread();
    /**
	 * the entity for thread running
	 * @param null {void } 无参数
	 * @return {int} 运行返回结果
	 */
    virtual int Run()=0;
    /**
	 * start thread
	 * @param null {void } 无参数
	 * @return {bool} 是否启动成功
	 */
    bool start();
    /**
	 * get thread ID
	 * @param null {void } 无参数
	 * @return {HANDLE_THREAD} 线程句柄
	 */
    HANDLE_THREAD getThreadID();
#ifdef __linux__
    //get thread status
    int getState();
    //wait for thread end
    void join();
    //wait for thread end in limit time
    void join(unsigned long millisTime);
#endif
public://val
#ifdef __linux__
    //threadStatus-new create
    static const int THREAD_STATUS_NEW = 0;
    //threadStatus-running
    static const int THREAD_STATUS_RUNNING = 1;
    //threadStatus-end
    static const int THREAD_STATUS_EXIT = -1;
#endif
private://func
#ifdef WIN32
	static void agent(void *p);
#else
    //get manner pointer of execution 
    static void* run0(void* pVoid);
    //manner of execution inside
    void* run1();
#endif
private://val
    HANDLE_THREAD hThread;
#ifdef __linux__
    //thread status
    int threadStatus;
#endif
};

#endif /* _MYTHREAD_H */

        消息队列类在队列内容进行变更时,需要通过互斥锁确保跨线程操作安全,互斥锁是由Mutex.h和Mutex.cpp源文件声明定义的,Mutex.h内容如下:

#ifndef PYMUTEX_H
#define PYMUTEX_H

#ifdef WIN32
//#include <windows.h>
#else
#include <pthread.h>
#endif

typedef void *HANDLE;

class IMutex
{
public:
	virtual ~IMutex() {}

	virtual void Lock() const = 0;
	virtual bool TryLock() const = 0;
	virtual void Unlock() const = 0;
};

class PYMutex : public IMutex
{
public:
	PYMutex();
	~PYMutex();

	virtual void Lock() const;
	virtual bool TryLock() const;
	virtual void Unlock() const;
private:
#ifdef _WIN32
	HANDLE m_mutex;
#else
	mutable pthread_mutex_t m_mutex;
#endif
};

#endif //PYMUTEX_H

        磁盘管理类,在进行磁盘信息读取和文件删除时,是借用了DiskSpace.h和DiskSpace.cpp源文件声明定义的磁盘空间统计和日志删除相关函数集来实现的,DiskSpace.h内容如下:

#ifndef DISK_SPACE_H
#define DISK_SPACE_H

/***********************************************************************
  *Copyright 2023-02-06, pyfree
  *
  *File Name       : DiskSpace.h
  *File Mark       : 
  *Summary         : 
  *磁盘空间统计和日志删除相关函数集
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/

#include <string>

namespace pyfree
{
  /**
	 * 根据指定磁盘获取磁盘的总空间和剩余空间
	 * @param _DiskStr {char} 磁盘目录,一般win指定{C,D},linux指定{/}
	 * @param _totalSize {int} 返回磁盘总空间
     * @param _freeSize {int} 返回磁盘剩余空间
	 * @return {int} 预留的返回值,暂指定是常量1
	 */
	int getDiskFreeSpace(char _DiskStr, int &_totalSize, int &_freeSize);
  /**
	 * 根据指定目录 扩展名 天数限制等删除目录下的文件,项目用于删除旧日志操作
	 * @param _dir {string}} 目录
	 * @param extname {string} 扩展名,如txt/ini/log等
     * @param dayForLimit_ {int} 指定天数,默认为0时会删除非当天的其他文件
	 * @return {void} 无返回
	 */
	void moveOldFile(std::string _dir, const std::string &extname, int dayForLimit_=0);
  /**
	 * 获取当天凌晨时刻的偏移时间,单位秒,与1970-01-01 00:00:00起
	 * @param deviation {int} 偏移秒数
	 * @return {int} 当天凌晨时刻的偏移时间.单位秒
	 */
	int getCurDayZeroClockTime(int deviation=0);
};

#endif

三、工程结构及编程测试

       3.1 工程目录结构

         按上述源文件设计,给出组织这些源文件的工程目录结构:

log_test
    bin                #测试程序输出
    build_win          #win编译中间文件存储
    build_linux        #Linux编译中间文件存储
    src
        DiskSpace.h    #磁盘访问、文件删除等功能实现
        DiskSpace.cpp
        Log.h          #日志类,单体模式,继承线程类
        Log.cpp
        LogDef.h       #日志信息定义
        Mutex.h        #互斥锁
        Mutex.cpp
        myThread.h     #线程类
        myThread.cpp
        pysleep.h      #线程睡眠等待统一宏转换设计
        queuedata.h    #消息队列类模板
        queuedata.cpp
        spaceMgr.h     #磁盘空间管理类,继承线程类
        spaceMgr.cpp
    test
        main.cpp      #测试程序代码
    CMakeLists.txt    #cmake工程
    compile.txt       #工程编译指令

        3.2 cmake工程配置

        CMakeLists.txt,cmake工程配置文件

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (log_test)
#
if(WIN32)
    message(STATUS "windows compiling...")
    add_definitions(-D_PLATFORM_IS_WINDOWS_)
	set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
  set(WIN_OS true)
else(WIN32)
    message(STATUS "linux compiling...")
    add_definitions( -D_PLATFORM_IS_LINUX_)
    add_definitions("-Wno-invalid-source-encoding")
	  # add_definitions("-O2")
    set(UNIX_OS true)
    set(_DEBUG true)
    
endif(WIN32)

#
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

# 指定源文件的目录,并将名称保存到变量
SET(source_h
    ${PROJECT_SOURCE_DIR}/src/pysleep.h
	${PROJECT_SOURCE_DIR}/src/Mutex.h
	${PROJECT_SOURCE_DIR}/src/myThread.h
	${PROJECT_SOURCE_DIR}/src/queuedata.h
	${PROJECT_SOURCE_DIR}/src/LogDef.h
	${PROJECT_SOURCE_DIR}/src/Log.h
	${PROJECT_SOURCE_DIR}/src/DiskSpace.h
	${PROJECT_SOURCE_DIR}/src/spaceMgr.h
  )
  
SET(source_cpp
    #
	${PROJECT_SOURCE_DIR}/src/Mutex.cpp
	${PROJECT_SOURCE_DIR}/src/myThread.cpp
	${PROJECT_SOURCE_DIR}/src/Log.cpp
	${PROJECT_SOURCE_DIR}/src/DiskSpace.cpp
	${PROJECT_SOURCE_DIR}/src/spaceMgr.cpp
	${PROJECT_SOURCE_DIR}/test/main.cpp
  )
  
#头文件目录
include_directories(${PROJECT_SOURCE_DIR}/src)

if (${UNIX_OS})

add_definitions(
  "-W"
  "-fPIC"
  "-Wall"
  # "-Wall -g"
  "-Werror"
  "-Wshadow"
  "-Wformat"
  "-Wpointer-arith"
  "-D_REENTRANT"
  "-D_USE_FAST_MACRO"
  "-Wno-long-long"
  "-Wuninitialized"
  "-D_POSIX_PTHREAD_SEMANTICS"
  "-DACL_PREPARE_COMPILE"
  "-Wno-unused-parameter"
  "-fexceptions"
  )
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")

link_directories()
# 指定生成目标
add_executable(log_test ${source_h} ${source_cpp})
#link
target_link_libraries(log_test 
  -lpthread -pthread -lz -lrt -ldl
)

endif(${UNIX_OS})

if (${WIN_OS})

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")

add_definitions(
  "-D_CRT_SECURE_NO_WARNINGS"
  "-D_WINSOCK_DEPRECATED_NO_WARNINGS"
  "-DNO_WARN_MBCS_MFC_DEPRECATION"
  "-DWIN32_LEAN_AND_MEAN"
)

link_directories()

if (CMAKE_BUILD_TYPE STREQUAL "Debug")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(log_testd ${source_h} ${source_cpp})

else(CMAKE_BUILD_TYPE)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(log_test ${source_h} ${source_cpp})

endif (CMAKE_BUILD_TYPE)

endif(${WIN_OS})

        compile.txt,编译指令指示文件

win:
cd log_test && mkdir build_win && cd build_win
cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
msbuild log_test.sln /p:Configuration="Release" /p:Platform="x64"

cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Debug ..
msbuild log_test.sln /p:Configuration="Debug" /p:Platform="x64"

Linux:
cd log_test
mkdir build_linux
cd build_linux
cmake ..
make

           3.3 日志类调用,测试案例

         测试工程文件main.cpp:

#include <stdio.h>
#include <stdlib.h>

#include "pysleep.h"
#include "Log.h"

namespace GlobalVar {
	extern char disk_DescStr;				//磁盘名
	extern unsigned int disk_space_limit;	//剩余空间限制为*MB
	extern std::string logdir;				//后期记录日志目录重配置文件读取
	extern std::string logname;				//可设置为服务名
	extern std::string log_ext_flag;		//日志扩展名
};
//log_test.exe D 2000 log log
//log_test.exe D 
void arg_analysis(int argc, char *argv[]);

int main(int argc, char *argv[])
{
	arg_analysis(argc,argv);
	int i = 0;
	while(i<100)
	{
		CLogger::createInstance()->Log(LogLevel(rand()%5),"log test for [%d]",i++);
		pysleep(10);
	}
	//while(1){pysleep(10);}
	return 0;
}

void arg_analysis(int argc, char *argv[])
{
	for(int index=0; index<argc; index++)
	{
		switch(index)
		{
		case 0:
		{
			#ifdef WIN32
			std::string appname = std::string(argv[0]);
			GlobalVar::logname = appname.substr(0,appname.length()-4);
			#else
			GlobalVar::logname = std::string(argv[0]);
			#endif
		}
			break;
		case 1:
		{
			GlobalVar::disk_DescStr = argv[1][0];
		}
			break;
		case 2:
		{
			GlobalVar::disk_space_limit = static_cast<unsigned int>(atoi(argv[2]));
		}
			break;
		case 3:
		{
			GlobalVar::logdir = std::string(argv[3]);
		}
			break;
		case 4:
		{
			GlobalVar::log_ext_flag = std::string(argv[4]);
		}
			break;
		default:
			break;
		}
	}	
}

        3.4 编译及测试

        win:

         linux:

 四、补充源码

        部分源码前面已经给出(头文件),下面给出其他源码文件:

         pysleep.h

#ifndef _PY_SLEEEP_H_
#define _PY_SLEEEP_H_

#ifdef WIN32
#include <windows.h>
#define pysleep(x) Sleep(x)
#else
#define pysleep(x) usleep(1000*x)
#endif

#endif //_PY_SLEEEP_H_

        DiskSpace.cpp

#include "DiskSpace.h"

#include <time.h>
#include <list>
#include <vector>

#ifdef WIN32
#include <io.h>
#include <direct.h>
#include <Windows.h>
#else
#include <sys/statfs.h>
#include <dirent.h>
/*#include <sys/vfs.h> or <sys/statfs.h> */
#include <sys/vfs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#endif

#include "Log.h"

int pyfree::getDiskFreeSpace(char _DiskStr, int &_totalSize, int &_freeSize)
{
#ifdef WIN32
	BOOL fResult;
	unsigned _int64 i64FreeBytesToCaller;
	unsigned _int64 i64TotalBytes;
	unsigned _int64 i64FreeBytes;
	char dir[4] = { _DiskStr, ':', '\\' };
	fResult = GetDiskFreeSpaceEx(
		dir,
		(PULARGE_INTEGER)&i64FreeBytesToCaller,
		(PULARGE_INTEGER)&i64TotalBytes,
		(PULARGE_INTEGER)&i64FreeBytes);
	//GetDiskFreeSpaceEx function,get the disk space status,return BOOL type    
	if (fResult)//jude the disk is in work status by the return value   
	{
		_totalSize = static_cast<int>(static_cast<float>(i64TotalBytes) / 1024 / 1024);
		_freeSize = static_cast<int>(static_cast<float>(i64FreeBytesToCaller) / 1024 / 1024);

		//std::cout << " totalspace:" << _totalSize << " MB" << std::endl;//disk total size    
		//std::cout << " freespace:" << _freeSize << " MB" << std::endl;//disk free space sze   
	}
#endif // WIN32
#ifdef __linux__
	//printf("DiskFlag(%c)\n", _DiskStr);
	struct statfs diskInfo;
	statfs("/", &diskInfo);
	unsigned long long totalBlocks = diskInfo.f_bsize;
	unsigned long long totalSize = totalBlocks * diskInfo.f_blocks;
	_totalSize = static_cast<int>(totalSize >> 20);
	//printf("TOTAL_SIZE == %d MB\n", _totalSize);

	unsigned long long freeDisk = diskInfo.f_bfree*totalBlocks;
	_freeSize = static_cast<int>(freeDisk >> 20);
	//printf("DISK_FREE == %d MB\n", _freeSize);
#endif
	return 1;
}

//delete old file which is older taday for the file modify time
void pyfree::moveOldFile(std::string _dir, const std::string &extname, int dayForLimit_)
{
	try{
#ifdef WIN32
	_finddata_t fileInfo;
	intptr_t hFile;
	std::string filter = _dir;
	if (filter[filter.size() - 1] != '//' || filter[filter.size() - 1] != '\\') {
		filter.push_back('\\');
	}
	filter += "*.";
	filter += extname;

	//time_t file_time_ = time(NULL);
	time_t file_time_ = (time_t)getCurDayZeroClockTime(dayForLimit_*86400);
	//get
	hFile = _findfirst(filter.c_str(), &fileInfo);
	if (hFile == -1) {
		return;
	}
	std::string delOldFile = "";
	//find the oldest file by modify time 
	do {
		if (extname.empty())//no ext name, that is dir
		{
			if ((fileInfo.attrib & _A_SUBDIR)) {
				if (0 == strcmp(fileInfo.name, ".") || 0 == strcmp(fileInfo.name, ".."))
				{
					continue;
				}
			}
		}
		//if(fileInfo.time_write<_time)//modify time
		if (fileInfo.time_create<file_time_)//create time
		{
			file_time_ = fileInfo.time_create;
			delOldFile = _dir + "//" + (std::string(fileInfo.name));
		}
	} while (_findnext(hFile, &fileInfo) == 0);
	_findclose(hFile);
#endif

#ifdef linux
	//printf("moveOldFile(*.%s) 1 from(%s) \n",extname.c_str(),_dir.c_str());
	std::string curdir = _dir;
	if (curdir[curdir.size() - 1] != '/') {
		curdir.push_back('/');
	}
	DIR *dfd;
	if ((dfd = opendir(curdir.c_str())) == NULL)
	{
		CLogger::createInstance()->Log(eLog_WARNING,"open %s error with msg is: %s\n", curdir.c_str(), strerror(errno));
		return;
	}
	struct dirent    *dp;
	//time_t file_time_ = time(NULL);
	time_t file_time_ = (time_t)getCurDayZeroClockTime(dayForLimit_ * 86400);
	std::string delOldFile = "";
	while ((dp = readdir(dfd)) != NULL)
	{
		if (extname.empty())//no ext name, that is dir
		{
			if (dp->d_type == DT_DIR) {
				if (0 == strcmp(dp->d_name, ".") || 0 == strcmp(dp->d_name, ".."))
				{
					continue;
				}
			}
		}
		else {
			if (NULL == strstr(dp->d_name, extname.c_str()))
			{
				continue;
			}
		}
		std::string _path = _dir + "/";
		_path += dp->d_name;
		struct stat el;
		stat(_path.c_str(), &el);
		if (el.st_mtime<file_time_) {
			file_time_ = el.st_mtime;
			delOldFile = _path;
		}
	}
	if (NULL != dp) {
		delete dp;
		dp = NULL;
	}
	if (NULL != dfd) {
		closedir(dfd);
		dfd = NULL;
	}
#endif
	if (!delOldFile.empty())
	{
		//printf("get old file: %s \n", delOldFile.c_str());
		int ret = remove(delOldFile.c_str());
		if (0 != ret) {
			CLogger::createInstance()->Log(eLog_WARNING,"can't remove %s \n", delOldFile.c_str());
		}else{
			CLogger::createInstance()->Log(eLog_NOTICE,"success remove %s \n", delOldFile.c_str());
		}
		
	}
	}catch(...){
		CLogger::createInstance()->Log(eLog_ERROR,"moveOldFile(*.%s) from(%s) exception error\n"
			,extname.c_str(),_dir.c_str());
	}
}

int pyfree::getCurDayZeroClockTime(int deviation)
{
	int ZeroClockTime_ = 0;
	time_t cur_time_ = time(NULL);
	struct tm _tt;
#ifdef WIN32
	localtime_s(&_tt, &cur_time_);
#else
	localtime_r(&cur_time_,&_tt);
#endif
	//当日凌晨时刻
	_tt.tm_hour = 0;
	_tt.tm_min = 0;
	_tt.tm_sec = 0;
	ZeroClockTime_ = static_cast<int>(mktime(&_tt));
	ZeroClockTime_ -= deviation;//偏移时间
	return ZeroClockTime_;
}

        Log.cpp

#include "Log.h"
#include <time.h>
#include <sys/timeb.h>

#include <stdio.h>
#include <stdarg.h>
#include <string>

#ifdef __linux__
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#ifndef sprintf_s
#define sprintf_s sprintf
#endif

#else	//WIN32

#ifndef vsnprintf
#define vsnprintf vsnprintf_s
#endif 
#endif

#include "pysleep.h"
#include "spaceMgr.h"

namespace GlobalVar
{
	char disk_DescStr  = 'D';		//磁盘名
	unsigned int disk_space_limit = 2000;	//剩余空间限制为*MB,少于该值去删除日志释放空间
	std::string logdir = "log";		//后期记录日志目录重配置文件读取
	std::string logname = "pyfree";	//可设置为服务名
	std::string log_ext_flag = "log";//日志扩展名
};

CLogger* CLogger::m_pLogInstance = NULL;
CLogger::CLogger() 
	: running(true)
	, fpLog(NULL)
	, cur_log_path("log")
{
	char buf[256] = {0};
	sprintf_s(buf,"mkdir %s",GlobalVar::logdir.c_str());
	system(buf);
	this->start();
	disk_space_mgr = new DiskSpaceMgr(GlobalVar::disk_DescStr
		,GlobalVar::disk_space_limit,GlobalVar::logdir,GlobalVar::log_ext_flag);
	disk_space_mgr->start();
};

CLogger::~CLogger()
{
	delete disk_space_mgr;
	disk_space_mgr = NULL;
	running = false;
	if (NULL != fpLog)
	{
		fclose(fpLog);
		fpLog = NULL;
	}
};

CLogger* CLogger::createInstance( void )
{
	if (m_pLogInstance == NULL)
	{
		m_pLogInstance = new CLogger();
		return m_pLogInstance;
	}
	else
		return m_pLogInstance;
};

int CLogger::Run()
{
	MyLogStruct<> log_item;
	while (running) 
	{
		if(logs_cache.pop(log_item))
		{
			WriteLog(log_item.type, log_item.szBuffer);
			#ifdef _DEBUG
			#ifndef WIN32
			printf("Log::[%d]-->%s\n", getpid(), log_item.szBuffer);
			#else
			printf("Log::-->%s\n", log_item.szBuffer);
			#endif //WIN32
			#endif //_DEBUG
		}
		pysleep(1);
	}
	return 0;
};

void CLogger::Log(const eLogType type, const char* lpszFormat, ...)
{
	va_list args;
	MyLogStruct<> log_item;
	log_item.type = static_cast<int>(type);
	va_start(args, lpszFormat);
	vsnprintf(log_item.szBuffer, sizeof(log_item.szBuffer), lpszFormat, args);
	va_end(args); 
	logs_cache.add(log_item);
}

void CLogger::WriteLog( const int iMsgType, const char * strMsg)
{
	try {
		//system time to string
		std::string strTime_ = strTime();
		//file name
		std::string strPath = createFileName(strTime_.substr(0,10));	
		// log time and type desc
		std::string strTimeAndTypeDesc = createLogTimeAndTypeDesc(iMsgType, strTime_);
		WriteLogToFile(strPath,strTimeAndTypeDesc,strMsg);
	}
	catch (...) {
		printf("write log[%d]{%s}error\n", iMsgType, strMsg);
	}
}

void CLogger::WriteLogToFile( std::string strPath,std::string strTime_,const char * strMsg)
{
	if(cur_log_path!=strPath||NULL == fpLog)
	{
		if (NULL != fpLog)
		{
			fclose(fpLog);
			fpLog = NULL;
		}			
		//open
		#ifdef WIN32
		fopen_s(&fpLog, strPath.c_str(), "a+");
		#else
		fpLog = fopen(strPath.c_str(), "a+");
		#endif
	}
	if (NULL != fpLog)
	{
		fseek(fpLog, 0, SEEK_END);
		fwrite(strTime_.c_str(), strTime_.length(), 1, fpLog);
		fwrite(strMsg, strlen(strMsg), 1, fpLog);
		fwrite("\n", 1, 1, fpLog);
	}
}

std::string CLogger::createFileName(std::string strTime_)
{
	//file name
	std::string strPath = GlobalVar::logdir;	
#ifdef WIN32
	strPath += "\\";
#else
	strPath += "/";
#endif
	strPath += strTime_;
	strPath += "_";
	strPath += GlobalVar::logname;
	strPath += ".log";
	return strPath;
}

std::string CLogger::strTime()
{
	time_t tt;
	struct timeb tm0;
	struct tm tm1;
	char buf[64];

	ftime(&tm0);
	tt = tm0.time;
#ifdef WIN32
	localtime_s(&tm1, &tt);
#else
	localtime_r(&tt, &tm1);
#endif
	sprintf_s(buf, "%04d-%02d-%02d %02d:%02d:%02d.%03d "
		, tm1.tm_year + 1900, tm1.tm_mon + 1, tm1.tm_mday
		, tm1.tm_hour, tm1.tm_min, tm1.tm_sec, tm0.millitm);
	std::string strTime_ = buf;
	buf[10] = '\0';
	return strTime_;
}
//日志等级枚举值的字符串描述
std::string eLogTypeDesc[6] = {
	"[UNKNOWN] ",
	"[eLog_DEBUG] ",
	"[eLog_TRACE] ",
	"[eLog_NOTICE] ",
	"[eLog_WARNING] ",
	"[eLog_ERROR] "};
	
std::string CLogger::createLogTimeAndTypeDesc(const unsigned int iMsgType, std::string strTime_)
{
	std::string strTimeAndTypeDesc = strTime_;
	unsigned int msg_type = iMsgType;
	if(msg_type>6) msg_type = 0;	//非枚举值外的均为未知
	strTimeAndTypeDesc += eLogTypeDesc[msg_type];
	return strTimeAndTypeDesc;
};

/*以下是LogFunction*/
/*
*在queuedata.cpp中应用,该函数主要用来解决QueueData类嵌套调用CLogger的异常问题:
*“嵌套名指定中使用了不完全的类型”
*/
void reportLogCacheOver(const char *queue_desc,int size)
{
	//每溢出10次,报告一次
	CLogger::createInstance()->Log(eLog_WARNING,
		"add item to queue %s, but the size of QueueData is up to limmit size: %d.\n"
		, queue_desc, size);	
}

        Mutex.cpp

#include "Mutex.h"

#ifdef WIN32
#include <windows.h>
#endif
//#include <iostream>
#include <stdio.h>

PYMutex::PYMutex()
{
#ifdef _WIN32
	m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
	pthread_mutex_init(&m_mutex, NULL);
#endif
}

PYMutex::~PYMutex()
{
#ifdef _WIN32
	::CloseHandle(m_mutex);
#else
	pthread_mutex_destroy(&m_mutex);
#endif
}


void PYMutex::Lock() const
{
#ifdef _WIN32
	//DWORD d = WaitForSingleObject(m_mutex, INFINITE);
	WaitForSingleObject(m_mutex, INFINITE);
	/// \todo check 'd' for result
#else
	pthread_mutex_lock(&m_mutex);
#endif
}

bool PYMutex::TryLock() const
{
#ifdef _WIN32
    DWORD dwWaitResult = WaitForSingleObject(m_mutex, 0);  
	if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) {
		printf("thread WARNING: bad result from try-locking mutex\n");
	}
    return (dwWaitResult == WAIT_OBJECT_0) ? true : false; 
#else
	return (0==pthread_mutex_trylock(&m_mutex))?true:false;
#endif	
};

void PYMutex::Unlock() const
{
#ifdef _WIN32
	::ReleaseMutex(m_mutex);
#else
	pthread_mutex_unlock(&m_mutex);
#endif
}

        myThread.cpp

#include "myThread.h"

#ifdef WIN32
#include <windows.h>
#else
#include <stdio.h>
#endif 

MyThread::MyThread()
{
    #ifdef __linux__
    hThread = 0;
    threadStatus = THREAD_STATUS_NEW;
    #endif
}

MyThread::~MyThread()
{
    #ifdef WIN32
	WaitForSingleObject(hThread, INFINITE);
    #else
    join(10);
    #endif
}

int MyThread::Run()
{
    #ifdef WIN32
    printf("Base Thread\n");
    #else
    while(true){
        printf("thread is running!\n");
        sleep(100);
    }
    #endif
    return 0;
}

bool MyThread::start()
{
    #ifdef WIN32
    return NULL!=(hThread =(HANDLE_THREAD)_beginthread(agent, 0, (void *)this));
    #else
    return pthread_create(&hThread, NULL, run0, this) == 0;
    #endif
}

HANDLE_THREAD MyThread::getThreadID()
{
    return hThread;
}

#ifdef WIN32
void MyThread::agent(void *p)
{
    MyThread *agt = (MyThread *)p;
    agt->Run();
}

#else
void* MyThread::run0(void* pVoid)
{
    MyThread* p = (MyThread*) pVoid;
    p->run1();
    return p;
}

void* MyThread::run1()
{
    threadStatus = THREAD_STATUS_RUNNING;
    hThread = pthread_self();
    Run();
    threadStatus = THREAD_STATUS_EXIT;
    hThread = 0;
    pthread_exit(NULL);
}

int MyThread::getState()
{
    return threadStatus;
}

void MyThread::join()
{
    if (hThread > 0)
    {
        pthread_join(hThread, NULL);
    }
}

void MyThread::join(unsigned long millisTime)
{
    if (hThread == 0)
    {
        return;
    }
    if (millisTime == 0)
    {
        join();
    }else
    {
        unsigned long k = 0;
        while (threadStatus != THREAD_STATUS_EXIT && k <= millisTime)
        {
            usleep(100);
            k++;
        }
    }
}
#endif

        queuedata.cpp

#include "queuedata.h"

#include "LogDef.h"
#include "Log.h"

extern void reportLogCacheOver(const char*,int);

template <class T>
unsigned int  QueueData<T>::QSize = 100;

template <class T>
QueueData<T>::QueueData(std::string desc)
	: queue_desc(desc)
{
	init();
};

template <class T>
void QueueData<T>::init() 
{
	queue_overS = 0;
};

template <class T>
QueueData<T>::~QueueData()
{

}

//
template <class T>
int QueueData<T>::size()
{
	int ret = 0;
	m_Mutex.Lock();
	ret = static_cast<int>(datacache_queue.size());
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::isEmpty()
{
	bool ret = false;
	m_Mutex.Lock();
	ret = datacache_queue.empty();
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::getFirst(T &it) 
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		it = datacache_queue.front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::removeFirst() 
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		datacache_queue.pop_front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::pop(T &it)
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		it = datacache_queue.front();
		datacache_queue.pop_front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
};

template <class T>
bool QueueData<T>::getList(std::queue<T> &its,unsigned int sizel)
{
	m_Mutex.Lock();
	while (!datacache_queue.empty())
	{
		its.push(datacache_queue.front());
		datacache_queue.pop_front();
		if (its.size() >= sizel)
		{
			break;
		}
	}
	m_Mutex.Unlock();
	return !its.empty();
};

template <class T>
void QueueData<T>::add(T it) 
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_back(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		//每溢出10次,报告一次
		reportLogCacheOver(queue_desc.c_str(),QSize);
		queue_overS = 0;
	}
}

template <class T>
void QueueData<T>::add_front(T it)
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_front(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		reportLogCacheOver(queue_desc.c_str(),QSize);
		queue_overS = 0;
	}
}

template <class T>
void QueueData<T>::clear()
{
	m_Mutex.Lock();
	datacache_queue.clear();
	m_Mutex.Unlock();
	queue_overS = 0;
}

        spaceMgr.cpp

#include "spaceMgr.h"

#include "pysleep.h"
#include "DiskSpace.h"

DiskSpaceMgr::DiskSpaceMgr(char disk_, int fsl_
	, std::string dir_/*="log"*/, std::string ext_/*="log"*/)
	: DiskStr(disk_),freeSizeLimit(fsl_)
{
	DelInfo del_(dir_,ext_);
	dirs.push_back(del_);
}

DiskSpaceMgr::~DiskSpaceMgr(void)
{

}

int DiskSpaceMgr::Run()
{
	int i_totalSize = 0;
	int i_freeSize = 0;
	while (true)
	{
		if (pyfree::getDiskFreeSpace(DiskStr, i_totalSize, i_freeSize) > 0)
		{
			if (freeSizeLimit > i_freeSize)
			{
				for(unsigned int i=0; i<dirs.size(); ++i)
				{
					std::string ext = dirs.at(i).ext;
					pyfree::moveOldFile(dirs.at(i).dir, ext);
				}
			}
		}
		pysleep(10);
	}
	return 0;
}

void DiskSpaceMgr::add(std::string dir_,std::string ext_)
{
	DelInfo del_(dir_,ext_);
	dirs.push_back(del_);
}

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

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

相关文章

Java ”框架 = 注解 + 反射 + 设计模式“ 之 注解详解

Java ”框架 注解 反射 设计模式“ 之 注解详解 每博一文案 刹那间我真想令时光停住&#xff0c;好让我回顾自己&#xff0c;回顾失去的年华&#xff0c;缅怀哪个穿一身短小的连衣裙 和瘦窄的短衫的小女孩。让我追悔少年时代&#xff0c;我心灵的愚钝无知&#xff0c;它轻易…

oracle11g忘记system密码,重置密码

OPW-00001: 无法打开口令文件 cmd.exe 使用管理员身份登录 找到xxx\product\11.2.0\dbhome_1\database\PWDorcl.ora文件&#xff0c;删除 执行orapwd fileD:\app\product\11.2.0\dbhome_1\database\PWDorcl.ora passwordtiger (orapwd 在\product\11.2.0\dbhome_1\BIN目录下…

DolphinScheduler第一章:环境安装

系列文章目录 DolphinScheduler第一章&#xff1a;环境安装 文章目录系列文章目录前言一、环境准备1.上传文件2.数据库配置3.配置安装文件二、集群部署1.数据部署2.部署 DolphinScheduler3. DolphinScheduler 启停命令总结前言 我们现在开始学习hadoop中的DolphinScheduler组…

Vim 命令速查表

Vim 命令速查表 简介&#xff1a;Vim 命令速查表&#xff0c;注释化 vimrc 配置文件&#xff0c;经典 Vim 键盘图&#xff0c;实用 Vim 书籍&#xff0c;Markdown 格式&#xff0c;目录化检索&#xff0c;系统化学习&#xff0c;快速熟悉使用&#xff01; Vim 官网 | Vim | Vim…

小学生学Arduino---------点阵(三)动态的显示与清除

学习目标&#xff1a; 1、理解“整数值”的概念与使用 2、理解“N1”指令的意义 3、掌握“反复执行多次”指令的使用 4、掌握屏幕模块的清除功能指令 5、理解“反复执行”指令与“反复执行多次”指令的嵌套使用 6、搭建电路图 7、编写程序 效果&#xff1a; 整数包括&#xf…

HTTP cookie格式与约束

cookie是前端编程当中经常要使用到的概念&#xff0c;我们可以使用cookie利用浏览器来存放用户的状态信息保存用户做了一些什么事情。session是服务器端维护的状态。session又是如何和cookie关联起来。后面介绍cookie和session的使用。Cookie 是什么&#xff1f;RFC6265, HTTP …

2022-2023年营销报告(B站平台) | 5大行业势态、流量大盘全景洞察

一直以来&#xff0c;手持高活跃、高粘性用户群体的B站是行业用来观察年轻人消费习惯的重要平台。以至于用户群体的不断壮大带动了B站的商业价值。如今B站的商业舞台越来越大&#xff0c;不断地向外界招手&#xff0c;欢迎更多品牌积极加入到这个千万年轻人聚集的内容社区。为了…

如何使用Kadence Blocks构建迷人的Kadence产品网格

在本教程中&#xff0c;我将逐步教您如何使用 Kadence Blocks 构建一个五列Kadence产品网格&#xff0c;它可以作为全宽区块放置在您的博客文章的顶部。使用这个包含五列的产品网格是在文章顶部展示产品、对每个产品进行简要描述&#xff0c;然后包含一个供用户访问该站点的按钮…

校园社交平台【源码好优多】

简介 本项目是为满足大学生的校园社交需求而设计的&#xff0c;动态模块提供发布/删除/搜索/点赞/收藏/评论动态功能&#xff0c;个人模块提供关注与私信以及用户修改个人信息功能&#xff0c;聊天模块提供即时聊天功能。该项目为前后端分离项目并且后端实现分布式&#xff0c…

蓝桥杯入门即劝退(十九)两两交换链表

-----持续更新蓝桥杯入门系列算法实例-------- 如果你也喜欢Java和算法&#xff0c;欢迎订阅专栏共同学习交流&#xff01; 你的点赞、关注、评论、是我创作的动力&#xff01; -------希望我的文章对你有所帮助-------- 一、题目描述 给你一个链表&#xff0c;两两交换其中…

JavaEE简单示例——<select>中的查询参数传递和结果集封装自动映射关系

简单介绍&#xff1a; 在之前我们在讲SQL映射文件中的映射查询语句的<select>标签的时候&#xff0c;对其中的四个常用属性的讲解并不是那么的透彻&#xff0c;今天就来详细的解释<select>的四个常用属性的具体含义以及<select>标签在进行查询的时候查询参数…

LQB02控制LED灯,74HC138芯片,74HC02芯片,74HC573芯片。

一&#xff0c;硬件图解读。 二&#xff0c;控制LED需要的74HC595程序编程。 三&#xff0c;控制某个LED亮&#xff0c;其他保持不变&#xff0c;或者控制整8个LED&#xff0c;其他不变&#xff1b; 四&#xff0c;读取某个LED的状态&#xff0c;一秒时间读取一次&#xff0c;如…

mapreduce与yarn

文章目录一、MapReduce1.1、MapReduce思想1.2、MapReduce实例进程1.3、MapReduce阶段组成1.4、MapReduce数据类型1.5、MapReduce关键类1.6、MapReduce执行流程1.6.1、Map阶段执行流程1.6.2、Map的shuffle阶段执行流程1.6.3、Reduce阶段执行流程1.7、MapReduce实例WordCount二、…

版本控制软件SVN

SVN学习 1 版本控制软件定义及用途 版本控制软件是为适应软件配置管理的需要&#xff0c;控制软件的修改&#xff0c;减少混乱&#xff0c;提高软件生产效率&#xff0c;其是软件质量保证的重要环节软件配置管理是对软件修改进行标识、组织和控制的技术&#xff0c;用来协调和…

MASA MAUI Plugin (八)Android相册多选照片(Intent 方式)

背景 MAUI的出现&#xff0c;赋予了广大.Net开发者开发多平台应用的能力&#xff0c;MAUI 是Xamarin.Forms演变而来&#xff0c;但是相比Xamarin性能更好&#xff0c;可扩展性更强&#xff0c;结构更简单。但是MAUI对于平台相关的实现并不完整。所以MASA团队开展了一个实验性项…

代码随想录NO49 | 动态规划 _LeetCode1143.最长公共子序列 1035.不相交的线 53. 最大子序和

动态规划 _LeetCode1143.最长公共子序列 1035.不相交的线 53. 最大子序和今天继续子序列问题&#xff01; 1143.最长公共子序列 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符…

docker全解

目录说明docker简介为什么是docker容器与虚拟机比较容器发展简史传统虚拟机技术容器虚拟化技术docker能干什么带来技术职级的变化开发/运维&#xff08;Devops)新一代开发工程师Docker应用场景why docker&#xff1f;docker的优势docker和dockerHub官网Docker安装CentOS Docker…

10 种 Spring事务失效场景

10 种 Spring事务失效场景 1.概述 Spring针对Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事务 API&#xff0c;实现了一致的编程模型&#xff0c;而Spring的声明式事务功能更是提供了极其方便的事务配置方式&#xff0c;配合Spring Boot的自动…

supervisor看守进程

supervisor可以用于看守正在运行的服务&#xff0c;如果服务以外停止&#xff0c;他会在设置的时间间隔内重新启动该服务。 本测试使用的系统是TencentOS3.1. 安装supervisor最简单方法是使用yum。 yum install supervisor supervisor需要python3支持&#xff0c;因此请先确…

【Android玩机】跟大家聊聊面具Magisk的使用(安装、隐藏)

目录:1、Magisk中文网2、隐藏面具和Root&#xff08;一共3种方法&#xff09;1、Magisk中文网 &#xff08;1&#xff09;首先Magisk有一个中文网&#xff0c;对新手非常友好 &#xff08;2&#xff09;这网站里面主要包含&#xff1a;6 部分 &#xff08;3&#xff09;按照他给…