线程池以及日志、线程总结

news2025/2/11 9:08:34

一、线程池以及日志

1、基础线程池写法

主线程在main函数中构建一个线程池,初始化(Init)后开始工作(Start)

此时线程池中每个线程都已经工作起来了,只是任务队列中任务为空,所有线程处于休眠状态(通过线程同步中的条件变量实现,即pthread_cond_wait)

当主线程向任务队列中添加任务后,唤醒一个在休眠中的线程,让其执行任务,执行完后再进入休眠状态。

基础的线程池的写法:

ThreadPool.hpp:

#pragma once

#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "mythreadlib.hpp"
#define DEFAULT_THREAD_NUMS 5

template <typename T> // 什么类型的任务
class ThreadPool
{

private:
    void test(const std::string name)
    {
        while (1)
        {
            std::cout << name << " is running" << std::endl;
            sleep(1);
        }
    }
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool QueueIsEmpty()
    {
        return _task_queue.empty();
    }
    void Sleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void Wake()
    {
        pthread_cond_signal(&_cond);
    }
    void WakeAll()
    {
        pthread_cond_broadcast(&_cond);
    }

    void HandlerTask(const std::string name)
    {
        while (1)
        {
            LockQueue();
            while (QueueIsEmpty() && _isrunning)
            {
                _sleep_nums++;
                Sleep();
                _sleep_nums--;
            }
            if (QueueIsEmpty() && !_isrunning)
            {
                UnlockQueue();
                break;
            }
            // 苏醒过来 执行任务
            T t = _task_queue.front();
            _task_queue.pop();

            UnlockQueue();
            std::cout<<name<<" is running:";
            t(); // 执行任务和获取任务一定要分开,一个在锁内,一个在锁外
        }
    }

public:
    ThreadPool(int threadnums = DEFAULT_THREAD_NUMS)
        : _threadnums(threadnums), _isrunning(false), _sleep_nums(0)
    {
        //_my_threads.resize(_threadnums); 不能resize会有问题
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
    void Init()
    {
        fun_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);
        // fun_t func = std::bind(&ThreadPool::test, this, std::placeholders::_1);

        for (int i = 0; i < _threadnums; i++)
        {
            std::string name = "thread-" + std::to_string(i + 1);
            _my_threads.emplace_back(name, func); // 要将test改成处理Task的函数
        }
    }

    void Start()
    {
        _isrunning = true;
        for (auto &thread : _my_threads)
        {
            thread.Start();
        }
    }

    void Stop()
    {
        LockQueue();
        _isrunning = false;

        WakeAll();

        UnlockQueue();
    }

    void Enqueue(const T &in)
    {
        // 主线程向线程池中派发任务,让线程池中的线程执行任务

        LockQueue();
        if (_isrunning)
        {
            _task_queue.push(in);
            if(_sleep_nums>0) Wake();//只要有休眠的线程就进行唤醒
            
        }

        UnlockQueue();
    }

private:
    int _threadnums;
    std::vector<mythread> _my_threads;
    int _sleep_nums;
    bool _isrunning;           // 也是临界资源
    std::queue<T> _task_queue; // 临界资源
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};

mythreadlib.hpp:

#pragma once

#include <pthread.h>
#include <iostream>
#include <string>
#include <functional>

using fun_t = std::function<void(const std::string &)>;

class mythread
{

    // typedef void (*fun_t)(ThreadData *td);

public:
    mythread(const std::string name, fun_t func)
        : _name(name), _func(func)
    {
        _isrunning = false;
    }
    static void *threadRun(void *args)
    {
        mythread *thread = static_cast<mythread *>(args);
        // thread->_func(thread->_td);
        thread->_func(thread->_name);
        thread->_isrunning = false; // 运行结束
        return nullptr;
    }

    bool Start()
    {
        int n = pthread_create(&_tid, nullptr, threadRun, (void *)this);
        if (n != 0)
            return false;
        else
        {
            _isrunning = true;
            return true;
        }
    }
    void Stop()
    {
        if (_isrunning)
        {
            _isrunning = false;
            ::pthread_cancel(_tid);
        }
    }
    void Join()
    {
        pthread_join(_tid, nullptr);
    }

    ~mythread()
    {
        if (_isrunning)
        {
            Stop();
            Join();
        }
    }

private:
    pthread_t _tid;
    std::string _name;
    bool _isrunning;
    fun_t _func;
};

task.hpp:

#pragma once

#include<iostream>
#include<functional>

// typedef std::function<void()> task_t;
// using task_t = std::function<void()>;

// void Download()
// {
//     std::cout << "我是一个下载的任务" << std::endl;
// }


// 要做加法
class Task
{
public:
    Task()
    {
    }
    Task(int x, int y) : _x(x), _y(y)
    {
    }
    void Excute()
    {
        _result = _x + _y;
        std::cout<<result()<<std::endl;
    }
    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;
};

main.cpp:

#include"ThreadPool.hpp"
#include"task.hpp"
#include<ctime>
#include<unistd.h>

int main()
{
    //线程池
    srand(time(nullptr)^getpid());
    ThreadPool<Task> *thread_pool=new ThreadPool<Task>();
    thread_pool->Init();
    thread_pool->Start();
    while(1)
    {
        int num1=rand()%9+1;
        int num2=rand()%9+1;
        Task ADD(num1,num2);
        thread_pool->Enqueue(ADD);
        
        sleep(1);

    }
    return 0;
}

2、日志

(1)日志概念

日志:软件运行的记录信息,可以向显示器打印,也可以向文件中打印,可以根据信息的重要程度使用不同的打印格式。

(2)基本格式

【日志等级】【pid】【filename】【filenumber】【time】日志内容(支持可变参数)

其中日志等级可分为:DEBUG(调试)、INFO(信息)、 WARNING(警告) 、ERROR(错误) 、FATAL(致命的)

可变参数的处理:用va_list指向可变部分, vsnprintf()

29. C语言 可变参数详解-CSDN博客

使用宏优化日志格式。

最终形成的日志:

#pragma once

#include <ctime>
#include <string>
#include <unistd.h>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include "LockGuard.hpp"
#include <pthread.h>
#define FLUSHSCREEN 1
#define FLUSHFILE 2
const std::string default_file = "log.txt";

enum
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

class logmessage
{
public:
    std::string _level;
    int _id;
    std::string _filename;
    int _filenumber;
    std::string _time;
    std::string _content;
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case 1:
        return "DEBUG";
        break;
    case 2:
        return "INFO";
        break;
    case 3:
        return "WARNING";
        break;
    case 4:
        return "ERROR";
        break;
    case 5:
        return "FATAL";
        break;
    }
}

std::string CurrentTime()
{
    time_t now = time(nullptr);
    struct tm *curr_time = localtime(&now);
    char time_string[128];
    snprintf(time_string, sizeof(time_string), "%d-%02d-%02d %02d:%02d:%02d", curr_time->tm_year + 1900, // 02d表示的是控制输出格式为2位整数,不够2位的用0填充
             curr_time->tm_mon + 1,
             curr_time->tm_mday,
             curr_time->tm_hour,
             curr_time->tm_min,
             curr_time->tm_sec);
    return time_string;
}

pthread_mutex_t _mutex=PTHREAD_MUTEX_INITIALIZER;
class Log
{
public:
    Log(const std::string flush_file = default_file, int flush_type = FLUSHSCREEN)
        : _flush_file(flush_file), _flush_type(flush_type)
    {
        //pthread_mutex_init(&_mutex, nullptr);
    }
    ~Log()
    {
        //pthread_mutex_destroy(&_mutex);
    }

    void logMessage(int level, const std::string filename, int filenumber, const char *format, ...) // 可变参数
    {
        logmessage logm;
        logm._level = LevelToString(level);
        logm._id = getpid();
        logm._filename = filename;
        logm._filenumber = filenumber;
        va_list vl;
        va_start(vl, format);
        char log_info[1024];
        vsnprintf(log_info, sizeof(log_info), format, vl);
        va_end(vl);
        logm._content = log_info;
        logm._time = CurrentTime();
        FlushLog(logm);
    }
    void Enable(int type)
    {
        _flush_type = type;
    }
    void FlushLog(logmessage &log)
    {
        //pthread_mutex_lock(&_mutex);
        lockguard lock(&_mutex);
        switch (_flush_type)
        {
        case FLUSHSCREEN:
            FlushToScreen(log);
            break;
        case FLUSHFILE:
            FlushToFile(log);
            break;
        }
        //pthread_mutex_unlock(&_mutex);

    }
    void FlushToScreen(logmessage &log)
    {
        printf("[%s][%d][%s][%d][%s] %s", log._level.c_str(), log._id, log._filename.c_str(), log._filenumber, log._time.c_str(), log._content.c_str());
    }
    void FlushToFile(logmessage &log)
    {
        std::ofstream out(_flush_file, std::ios::app);
        char message[1024];
        snprintf(message, sizeof(message), "[%s][%d][%s][%d][%s] %s", log._level.c_str(), log._id, log._filename.c_str(), log._filenumber, log._time.c_str(), log._content.c_str());
        out.write(message, strlen(message));
        out.close();
    }

private:
    std::string _flush_file;
    int _flush_type;
    
};

// 优化
Log glog;

#define LOG(level, format, ...)                                            \
    do                                                                     \
    {                                                                      \
        glog.logMessage(level, __FILE__, __LINE__, format, ##__VA_ARGS__); \
    } while (0) // 带两个#是为了处理可变参数为空的情况

#define EnableScreen()  \
    do                  \
    {                   \
        glog.Enable(1); \
    } while (0)

#define EnableFile()    \
    do                  \
    {                   \
        glog.Enable(2); \
    } while (0)

3、单例模式

单例模式:一个类只创建一个对象

单例模式实现方式有两种:懒汉模式和饿汉模式。

4、最终代码

经过单例模式的修改,最终形成的线程池代码:

Threadpool.hpp:

#pragma once

#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "mythreadlib.hpp"
#include "Log.hpp"
#include "LockGuard.hpp"
#define DEFAULT_THREAD_NUMS 5

template <typename T> // 什么类型的任务
class ThreadPool
{

private:
    void test(const std::string name)
    {
        while (1)
        {
            std::cout << name << " is running" << std::endl;
            sleep(1);
        }
    }
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool QueueIsEmpty()
    {
        return _task_queue.empty();
    }
    void Sleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void Wake()
    {
        pthread_cond_signal(&_cond);
    }
    void WakeAll()
    {
        pthread_cond_broadcast(&_cond);
    }

    void HandlerTask(const std::string name)
    {
        while (1)
        {
            LockQueue();
            while (QueueIsEmpty() && _isrunning)
            {
                _sleep_nums++;
                LOG(DEBUG, "%s begin sleep\n", name.c_str());
                Sleep();
                LOG(DEBUG, "%s wake up\n", name.c_str());

                _sleep_nums--;
            }
            if (QueueIsEmpty() && !_isrunning)
            {
                UnlockQueue();
                break;
            }
            // 苏醒过来 执行任务
            T t = _task_queue.front();
            _task_queue.pop();

            UnlockQueue();

            t(); // 执行任务和获取任务一定要分开,一个在锁内,一个在锁外
            LOG(DEBUG, "%s Handler task done\n", name.c_str());
        }
    }
    ThreadPool(int threadnums = DEFAULT_THREAD_NUMS)
        : _threadnums(threadnums), _isrunning(false), _sleep_nums(0)
    {
        //_my_threads.resize(_threadnums); 不能resize会有问题
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
    ThreadPool(const ThreadPool<T> &) = delete;
    void operator=(const ThreadPool<T> &) = delete;//设置拷贝构造和=操作符重载为delete

    void Init()
    {
        fun_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);
        // fun_t func = std::bind(&ThreadPool::test, this, std::placeholders::_1);

        for (int i = 0; i < _threadnums; i++)
        {
            std::string name = "thread-" + std::to_string(i + 1);
            _my_threads.emplace_back(name, func); // 要将test改成处理Task的函数
            LOG(DEBUG, "construct %s done,init success\n", name.c_str());
        }
    }

    void Start()
    {
        _isrunning = true;
        for (auto &thread : _my_threads)
        {
            thread.Start();
            LOG(DEBUG, "start %s done\n", thread.getname().c_str());
        }
    }

public:
    static ThreadPool<T> *Get_Instance()
    {
        if (_tp == nullptr)
        {
            lockguard lock(&_tp_mutex);
            if (_tp == nullptr)
            {
                _tp = new ThreadPool<T>;
                _tp->Init();
                _tp->Start();
                LOG(DEBUG,"Thread pool create success!\n");
            }
        }

        return _tp;
    }
    void Stop()
    {
        LockQueue();
        _isrunning = false;

        WakeAll();

        UnlockQueue();
        LOG(DEBUG,"all thread stop success!\n");
    }

    void Enqueue(const T &in)
    {
        // 主线程向线程池中派发任务,让线程池中的线程执行任务

        LockQueue();
        if (_isrunning)
        {
            _task_queue.push(in);
            if (_sleep_nums > 0)
                Wake(); // 只要有休眠的线程就进行唤醒
        }

        UnlockQueue();
    }

private:
    int _threadnums;
    std::vector<mythread> _my_threads;
    int _sleep_nums;
    bool _isrunning;           // 也是临界资源
    std::queue<T> _task_queue; // 临界资源
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    static ThreadPool<T> *_tp;
    static pthread_mutex_t _tp_mutex;
};

template <typename T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;

template <typename T>
pthread_mutex_t ThreadPool<T>::_tp_mutex = PTHREAD_MUTEX_INITIALIZER;

main.cpp:

#include "ThreadPool.hpp"
#include "task.hpp"
#include "Log.hpp"
#include <ctime>
#include <unistd.h>

/*int main()
{
    //线程池
    srand(time(nullptr)^getpid());
    ThreadPool<Task> *thread_pool=new ThreadPool<Task>();
    thread_pool->Init();
    thread_pool->Start();
    while(1)
    {
        int num1=rand()%9+1;
        int num2=rand()%9+1;
        Task ADD(num1,num2);
        thread_pool->Enqueue(ADD);

        sleep(1);

    }
    return 0;
}*/

/*int main()
{
    //测试未优化的日志
    Log log1;
    log1.logMessage(DEBUG, __FILE__, __LINE__, "this is a logmessage of %d,%f\n", 30, 3.14);

    sleep(1);
    log1.logMessage(DEBUG, __FILE__, __LINE__, "this is a logmessage of %d,%f\n", 30, 3.14);
    log1.Enable(FLUSHFILE);
    sleep(1);
    log1.logMessage(DEBUG, __FILE__, __LINE__, "this is a logmessage of %d,%f\n", 30, 3.14);
    sleep(1);
    log1.logMessage(DEBUG, __FILE__, __LINE__, "this is a logmessage of %d,%f\n", 30, 3.14);
    return 0;
}*/

/*int main()
{
    EnableScreen();
    //LOG(DEBUG,"YES");//依然支持
    LOG(DEBUG,"this is a logmessage of %d,%f\n", 30, 3.14);
    LOG(WARNING,"this is a logmessage of %d,%f\n", 30, 3.14);
    LOG(ERROR,"this is a logmessage of %d,%f\n", 30, 3.14);
    LOG(INFO,"this is a logmessage of %d,%f\n", 30, 3.14);


    EnableFile();
    LOG(DEBUG,"this is a logmessage of %d,%f\n", 30, 3.14);
    LOG(WARNING,"this is a logmessage of %d,%f\n", 30, 3.14);
    LOG(ERROR,"this is a logmessage of %d,%f\n", 30, 3.14);
    LOG(INFO,"this is a logmessage of %d,%f\n", 30, 3.14);

    return 0;
}*/

/*int main()
{
    //线程池
    srand(time(nullptr)^getpid());
    ThreadPool<Task> *thread_pool=new ThreadPool<Task>();
    thread_pool->Init();
    thread_pool->Start();
    while(1)
    {
        int num1=rand()%9+1;
        int num2=rand()%9+1;
        Task ADD(num1,num2);
        thread_pool->Enqueue(ADD);
        LOG(INFO,"task is product:%s\n",ADD.debug().c_str());

        sleep(1);

    }
    return 0;
}*/

int main()
{
    // 单例模式线程池的测试
    srand(time(nullptr) ^ getpid());
    int cnt=10;
    while (cnt--)
    {
        int num1 = rand() % 9 + 1;
        int num2 = rand() % 9 + 1;
        Task ADD(num1, num2);
        ThreadPool<Task>::Get_Instance()->Enqueue(ADD);
        sleep(1);
    }
    ThreadPool<Task>::Get_Instance()->Stop();

    return 0;
}

二、线程概念拓展

1、可重入和线程安全

(1)概念

(2)理解

一个函数是可重入的,那么它一定是线程安全的;但一个线程是安全的,不表示该线程内的函数是可重入的,其内部的函数可能是不可重入的,例如加锁但不释放的函数,是线程安全但不可重入函数。

2、死锁

(1)死锁概念

一个线程一把锁也可能形成死锁,例如对同一个锁加锁两次而未释放。

但更多的情况是俩个执行流,两把锁,要推进代码需要两把锁都申请到,但俩个执行流分别申请一把锁,而不释放,都同时申请对方的锁。

(2)死锁的必要条件

(3)避免死锁

破坏必要条件:

①尽量不使用锁

②当某优先级比较高的线程申请不到锁的时候强制释放锁

③申请锁的代码的顺序要一致

3、STL,智能指针和线程安全

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

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

相关文章

Vue 响应式渲染 - 过滤应用

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue响应式渲染综合 - 过滤应用 目录 过滤应用 引入vue Vue设置 设置页面元素 模糊查询过滤实现 函数表达式实现 总结 过滤应用 综合响应式渲染做一个输入框&#xff0c;用来实现&#xff1b;搜索输入框关键词符合列表。…

【ThreeJS Basics 1-3】Hello ThreeJS,实现第一个场景

文章目录 环境创建一个项目安装依赖基础 Web 页面概念解释编写代码运行项目 环境 我的环境是 node version 22 创建一个项目 首先&#xff0c;新建一个空的文件夹&#xff0c;然后 npm init -y , 此时会快速生成好默认的 package.json 安装依赖 在新建的项目下用 npm 安装依…

深入理解动态代理

为什么需要动态代理 对于代码的增强逻辑我们是清楚具体实现的,一种方式是增强逻辑作为委托类,被其他业务类调用, 这样会有很多重复代码,而且,当需要根据动态参数来决定增强逻辑时,重复代码会更多,逻辑会更不清晰 二,也是动态代理产生的原始需求,解决类爆照问题, 所以…

Cherry Studio之DeepSeek联网/本地,建属于自己的AI助理!

上一篇文章&#xff0c;讲了DeepSeek-R1部署到本地的方法。这一篇文章&#xff0c;我们让DeepSeek再一次升级&#xff0c;通过图形化界面来交互&#xff0c;从而变成我们的AI助理&#xff0c;让DeepSeek R1发挥最大实力&#xff01; 首选需要借助硅基流动的API接口&#xff0c…

IGBT的两级关断

IGBT&#xff08;绝缘栅双极型晶体管&#xff09;的两级关断&#xff08;Two-stage turn-off&#xff09;是一种优化关断过程的方法&#xff0c;主要用于减少关断时的电压过冲和dv/dt&#xff08;电压变化率&#xff09;过高的问题&#xff0c;特别是在大功率应用中&#xff08…

【STM32】ADC

本次实现的是ADC实现数字信号与模拟信号的转化&#xff0c;数字信号时不连续的&#xff0c;模拟信号是连续的。 1.ADC转化的原理 模拟-数字转换技术使用的是逐次逼近法&#xff0c;使用二分比较的方法来确定电压值 当单片机对应的参考电压为3.3v时&#xff0c;0~ 3.3v(模拟信号…

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 0基础…

Chapter3:结构化程序设计

参考书籍&#xff1a;《C#边做边学》&#xff1b; 3.结构化程序设计 3.1 结构化程序设计的3种基本结构 顺序结构&#xff1a;先执行 A {\rm A} A语句&#xff0c;再执行 B {\rm B} B语句&#xff0c;两者是顺序执行的关系&#xff1b; 选择结构&#xff1a;根据所定选择条件为…

白话文实战Nacos(保姆级教程)

前言 上一篇博客 我们创建好了微服务项目,本篇博客来体验一下Nacos作为注册中心和配置中心的功能。 注册中心 如果我们启动了一个Nacos注册中心,那么微服务比如订单服务,启动后就可以连上注册中心把自己注册上去,这过程就是服务注册。每个微服务,比如商品服务都应该注册…

智能理解 PPT 内容,快速生成讲解视频

当我们想根据一版 PPT 制作出相对应的解锁视频时&#xff0c;从撰写解锁词&#xff0c;录制音频到剪辑视频&#xff0c;每一个环节都需要投入大量的时间和精力&#xff0c;本方案将依托于阿里云函数计算 FC 和百炼模型服务&#xff0c;实现从 PPT 到视频的全自动转换&#xff0…

IEC61850标准下的数据和数据模型服务的详细介绍

目录 一、摘要 二、概述 三、详细介绍 1、读服务器目录(GetServerDirectory) 2、读逻辑设备目录(GetLogicalDeviceDirectory) 3、读逻辑节点目录(GetLogicalNodeDirectory) 4、读全部数据值(GetAllDataValues) 5、读数据值(GetDataValues) 6、设置数据值(SetDataValues…

R语言LCMM多维度潜在类别模型流行病学研究:LCA、MM方法分析纵向数据

全文代码数据&#xff1a;https://tecdat.cn/?p39710 在数据分析领域&#xff0c;当我们面对一组数据时&#xff0c;通常会有已知的分组情况&#xff0c;比如不同的治疗组、性别组或种族组等&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 然而&#xff0c;…

5. 【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--Nacos

一、什么是Nacos Nacos 是阿里巴巴开源的一款云原生应用基础设施&#xff0c;它旨在简化微服务架构中服务治理和配置管理的复杂性。通过 Nacos&#xff0c;服务在启动时可以自动注册&#xff0c;而其他服务则可以通过名称来查找并访问这些注册好的实例。同时&#xff0c;Nacos…

VUE项目中实现权限控制,菜单权限,按钮权限,接口权限,路由权限,操作权限,数据权限实现

VUE项目中实现权限控制&#xff0c;菜单权限&#xff0c;按钮权限&#xff0c;接口权限&#xff0c;路由权限&#xff0c;操作权限&#xff0c;数据权限实现 权限系统分类&#xff08;RBAC&#xff09;引言菜单权限按钮权限接口权限路由权限 菜单权限方案方案一&#xff1a;菜单…

【网络安全】服务器安装Docker及拉取镜像教程

文章目录 1. 安装 Docker2. 拉取镜像3. 运行 Ubuntu 容器4. 执行相关操作5. 退出并停止容器1. 安装 Docker # 更新软件包索引 sudo apt update# 安装必要的依赖 sudo apt install -y ca-certificates curl gnupg

elementplus 使用日期时间选择器,设置可选范围为前后大于2年且只能选择历史时间不能大于当前时间点

需求&#xff1a;时间选择器可选的时间范围进行限制&#xff0c;-2年<a<2年且a<new Date().getTime()核心&#xff1a;这里需要注意plus版没有picker-options换成disabled-date属性了&#xff0c;使用了visible-change和calendar-change属性逻辑&#xff1a;另设一个参…

将 AMD Zynq™ RFSoC 扩展到毫米波领域

目录 将 AMD Zynq™ RFSoC 扩展到毫米波领域Avnet XRF RFSoC 系统级模块适用于 MATLAB 的 Avnet RFSoC Explorer 工具箱5G mmWave PAAM 开发平台突破性的宽带毫米波波束成形特征&#xff1a;OTBF103 Mathworks Simulink 模型优化毫米波应用中的射频信号路径 用于宽带毫米波上/下…

Redis企业开发实战(五)——点评项目之分布式锁Redission与秒杀优化

目录 一、Redisson (一)Redisson基本介绍 (二)Redisson入门 1.引入依赖 2.配置Redisson客户端 3.使用Redission的分布式锁 4.tryLock参数解析 4.1tryLock() 4.2tryLock(long waitTime, TimeUnit unit) 4.3tryLock(long waitTime, long leaseTime, TimeUnit unit) 4…

IDEA安装离线插件(目前提供了MavenHelper安装包)

目录 1、离线安装方式2、Maven Helper 1、离线安装方式 首先访问 IDEA插件网站 下载离线插件安装包&#xff0c;操作如下&#xff1a; 然后打开IDEA的Settings配置&#xff0c;点击Plugins&#xff0c;点击右侧设置按钮&#xff08;齿轮&#xff09;&#xff0c;选择Install P…

LabVIEW 开发航天项目软件

在航天项目软件开发中&#xff0c;LabVIEW 凭借其图形化编程优势被广泛应用。然而&#xff0c;航天项目的高可靠性、高精度及复杂环境适应性要求&#xff0c;使得在使用 LabVIEW 开发时&#xff0c;有诸多关键要点需要特别关注。本文将详细分析在开发航天项目软件时需要重点注意…