线程池:线程池的实现 | 日志

在这里插入图片描述

🌈个人主页: 南桥几晴秋
🌈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;
};

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

数值计算的程序设计问题举例

### 数值计算的程序设计问题 #### 1. 结构静力分析计算 **涉及领域**&#xff1a;工程力学、建筑工程 **主要问题**&#xff1a;线性代数方程组&#xff08;Linear Algebraic Equations&#xff09; **解释说明**&#xff1a; 在结构静力分析中&#xff0c;我们需要解决复杂的…

C++:类中的特殊关键字,运算重载符

1.My_string类中重载以下的运算符&#xff1a; 、[] 、>、<、、>、<、&#xff01;、、输入输出(>>、<<) 主函数&#xff1a; #include <iostream> #include "my_string.h"using namespace std;int main() {My_string s1("cat…

基于SpringBoot+Vue的个人健康管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

【STM32-HAL库】自发电型风速传感器(使用STM32F407ZGT6)(附带工程下载链接)

一、自发电型风速传感器介绍 自发电型风速传感器&#xff0c;也称为风力发电型风速传感器或无源风速传感器&#xff0c;是一种不需要外部电源即可工作的风速测量设备。这种传感器通常利用风力来驱动内部的发电机构&#xff0c;从而产生电能来供电测量风速的传感器部分。以下是自…

GS-SLAM论文阅读笔记--GEVO

前言 这篇文章看着就让人好奇。众所周知&#xff0c;高斯是一个很不错的建图方法&#xff0c;但是本文的题目居然是只用高斯进行单目VO&#xff0c;咱也不知道这是怎么个流程&#xff0c;看了一下作者来自于MIT&#xff0c;说不定是个不错的工作&#xff0c;那就具体看看吧&am…

springboot实战学习(10)(ThreadLoacl优化获取用户详细信息接口)(重写拦截器afterCompletion()方法)

接着学习。之前的博客的进度&#xff1a;完成用户模块的注册接口的开发以及注册时的参数合法性校验、也基本完成用户模块的登录接口的主逻辑的基础上、JWT令牌"的组成与使用、完成了"登录认证"&#xff08;生成与验证JWT令牌&#xff09;以及完成获取用户详细信…

APISIX 联动雷池 WAF 实现 Web 安全防护

Apache APISIX 是一个动态、实时、高性能的云原生 API 网关&#xff0c;提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。 雷池是由长亭科技开发的 WAF 系统&#xff0c;提供对 HTTP 请求的安全请求&#xff0c;提供完整的 API 管理和…

【CKA】二、节点管理-设置节点不可用

2、节点管理-设置节点不可用 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 先设置节点不可用&#xff0c;然后驱逐节点上的pod 这道题就两条命令&#xff0c;直接背熟就行。 也可以查看帮助 kubectl cordon -h kubectl drain -h 参数详情&#xff1a; –delete-empty…

YOLO11震撼发布!

非常高兴地向大家介绍 Ultralytics YOLO系列的新模型&#xff1a; YOLO11&#xff01; YOLO11 在以往 YOLO 模型基础上带来了一系列强大的功能和优化&#xff0c;使其速度更快、更准确、用途更广泛。主要改进包括 增强了特征提取功能&#xff0c;从而可以更精确地捕捉细节以更…

在树莓派上基于 LNMP 搭建 Nextcloud

原文链接&#xff1a;https://blog.iyatt.com/?p17296 环境 树莓派CM4raspios 20240704 Debian 12 arm64 搭建 LNMP 环境 安装 Nginx sudo apt update sudo apt install -y nginx安装 php 及功能组件支持 参考&#xff1a;https://docs.nextcloud.com/server/latest/adm…

EXCEL图片链接快速批量转成图片

EXCEL图片链接快速批量转成图片 直接上图 "<table><img src"&C1&" height50 width50></table>"复制F列到txt文件&#xff0c;暂时放置 全选复制&#xff0c;然后插入一列&#xff0c;粘贴到新的一列中去如图一所示。 ps&…

光通信——PON技术

PON网络结构 PON&#xff08;Passive Optical Network&#xff0c;无源光网络&#xff09;系统的基本组成包括OLT&#xff08;Optical Line Terminal&#xff0c;光线路终端&#xff09;、ODN&#xff08;Optical Distribution Network&#xff0c;光分配单元&#xff09;和ON…

初学51单片机之I2C总线与E2PROM二

总结下上篇博文的结论&#xff1a; 1&#xff1a;ACK信号在SCL为高电平期间会一直保持。 2&#xff1a;在字节数据传输过程中如果发送电平跳变&#xff0c;那么电平信号就会变成重复起始或者结束的信号。&#xff08;上篇博文的测试方法还是不能够明确证明这个结论&#xff0…

深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制

我的主页&#xff1a;2的n次方_ 1. JVM 内存区域划分 程序计数器&#xff08;空间比较小&#xff09;。保存了下一条要执行的指令的地址&#xff08;指向元数据区指令的地址&#xff09;堆。JVM 最大的空间&#xff0c;new 出来的对象都在堆上栈。函数中的局部变量&#x…

linux服务器部署filebeat

# 下载filebeat curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.17.23-linux-x86_64.tar.gz # 解压 tar xzvf filebeat-7.17.23-linux-x86_64.tar.gz# 所在位置&#xff08;自定义&#xff09; /opt/filebeat-7.17.23-linux-x86_64/filebeat.ym…

一篇文章快速学会docker容器技术

目录 一、Docker简介及部署方法 1.1Docker简介 1.1.1什么是docker 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 二 、部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服务 2.2.…

C嘎嘎入门篇:类和对象(2)

前言&#xff1a; 上一篇小编讲了类和对象&#xff08;1&#xff09;&#xff0c;当然&#xff0c;在看这篇文章之前&#xff0c;读者朋友们一定要掌握好前面的基础内容&#xff0c;因为这篇和前面息息相关&#xff0c;废话不多说&#xff0c;下面小编就加快步伐&#xff0c;开…

css的背景background属性

CSS的background属性是一个简写属性&#xff0c;它允许你同时设置元素的多个背景相关的子属性。使用这个属性可以简化代码&#xff0c;使其更加清晰和易于维护。background属性可以设置不同的子属性。 background子属性 定义背景颜色 使用background-color属性 格式&#x…

ARM Process state -- CPSR

Holds PE status and control information. 保存PE状态和控制信息。 N, bit [31] Negative condition flag. Set to bit[31] of the result of the last flag-setting instruction. If the result is regarded as a twos complement signed integer, then N is set to 1 if…

Docker学习和部署ry项目

文章目录 停止Docker重启设置开机自启执行docker ps命令&#xff0c;如果不报错&#xff0c;说明安装启动成功2.然后查看数据卷结果3.查看数据卷详情结果4.查看/var/lib/docker/volumes/html/_data目录可以看到与nginx的html目录内容一样&#xff0c;结果如下&#xff1a;5.进入…