Linux——多线程(五)

news2024/12/28 20:47:06

1.线程池

1.1初期框架

thread.hpp

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

namespace ThreadModule
{
    using func_t = std::function<void()>;

    class Thread
    {
    public:
        void Excute()
        {
            _func();
        }
    public:
        Thread(func_t func, std::string name="none-name")
            : _func(func), _threadname(name), _stop(true)
        {}
        static void *threadroutine(void *args) //注意:类成员函数,形参是有this指针的
        {
            Thread *self = static_cast<Thread *>(args);
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, threadroutine, this);
            if(!n)
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }
        void Detach()
        {
            if(!_stop)
            {
                pthread_detach(_tid);
            }
        }
        void Join()
        {
            if(!_stop)
            {
                pthread_join(_tid, nullptr);
            }
        }
        std::string name()
        {
            return _threadname;
        }
        void Stop()
        {
            _stop = true;
        }
        ~Thread() {}

    private:
        pthread_t _tid;//线程tid
        std::string _threadname;//线程名字
        func_t _func;//线程所要执行的函数
        bool _stop;//判断线程是否停止
    };
}

 ThreadPool.hpp 

#include<vector>
#include<unistd.h>
#include<string>
#include<queue>
#include"Thread.hpp"

using namespace ThreadModule;
const int g_thread_num = 3;//默认线程数
// 线程池->一批线程,一批任务,有任务push、有任务pop,本质是: 生产消费模型
template <typename T>
class ThreadPool
{
public:
    ThreadPool(int threadnum=g_thread_num)//构造函数
        :_threadnum(threadnum)
        , _waitnum(0)
        , _isrunning(false)
    {
        pthread_mutex_init(&_mutex,nullptr);//初始化锁
        pthread_cond_init(&_cond,nullptr);//初始化条件变量
    }
    void Print()
    {
        while(true)
        {
            std::cout<<"我是一个线程"<<std::endl;
            sleep(1);
        }
    }
    void InitThreadPool()
	{
	    // 指向构建出所有的线程,并不启动
        for (int num = 0; num < _threadnum; num++)
	    {
	        std::string name = "thread-" + std::to_string(num + 1);
            _threads.emplace_back(Print,name);//线程处理函数是Print,注意这里有问题
	    }
	    _isrunning = true;
	}
    void Start()//启动线程池
    {
        for(auto &thread:_threads)
        {
            thread.Start();
            std::cout<<thread.name()<<"线程:启动成功"<<std::endl;
        }
    }



    void Wait()
    {
        for(auto &thread:_threads)
        {
            thread.Join();
        }
    }

    // bool Enqueue(const T &t)
    // {
    // }


    ~ThreadPool()//析构
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    int _threadnum;//线程的数量
    std::vector<Thread> _threads;//用vector来存线程
    std::queue<T> _task_queue;//任务队列

    pthread_mutex_t _mutex;//锁
	pthread_cond_t _cond;//条件变量
	int _waitnum;//有几个线程阻塞
    bool _isrunning;//判断线程池是否在运行
};

main.cc

#include <iostream>	
#include <string>
#include <memory>
#include "threadpool.hpp"	
	
int main()
{
	std::unique_ptr<ThreadPool<int>> tp(new ThreadPool<int>()); 
	tp->InitThreadPool();
	tp->Start();
	sleep(5);

    tp->Wait();
    return 0;
}

 

 此时会报错:无效使用非静态成员函数...

主要原因是成员函数包含this指针而thread.hpp中线程所要执行函数的参数为空:using func_t = std::function<void()>;,导致参数类型不匹配

有两种解决方法

 方法一:在Print函数前面加上static

    static void Print()
    {
        while(true)
        {
            std::cout<<"我是一个线程"<<std::endl;
            sleep(1);
        }
    }

 

方法二:在初始化线程池时用bind绑定ThreadPool内部的Print方法,缺省地设置参数this,就是将this参数默认的绑定到Print方法上,这样一来就和thread.hpp中的参数匹配上了 

    void InitThreadPool()
	{
	    // 指向构建出所有的线程,并不启动
        for (int num = 0; num < _threadnum; num++)
	    {
	        std::string name = "thread-" + std::to_string(num + 1);
            //_threads.emplace_back(Print,name);//线程处理函数是Print
            _threads.emplace_back(std::bind(&ThreadPool::Print,this),name);
	    }
	    _isrunning = true;
	}

  也是成功运行

就算后面我们需要更改线程的参数
 那么也可以在初始化函数那里固定式的绑定参数了

不需要再去单独给线程设计参数对象了 

一个类的成员方法设计成另一个类的回调方法,常见的实现就是这种

类的成员方法也可以成为另一个类的回调方法,方便我们继续类级别的互相调用

 

1.2代码完善

 

接下来就是如何入队列以及我们的新线程应该做什么任务...

处理任务:每一个线程进来的时候都需要去任务队列中获取任务,所以我们首当其冲的就要对任务队列给它锁住

任务队列的加锁、解锁以及线程的等待与唤醒(条件变量)

private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadWakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }

 处理任务

    void HandlerTask(std::string name)//线程处理任务
    {
        while (true)
        {
            //加锁
            LockQueue();
            //任务队列中不一定有数据,如果任务队列为空且线程池在跑,那么就阻塞住
            while(_task_queue.empty()&&_isrunning)
            {
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }
            //如果任务队列是空的,然后线程池又退出了,那么就没必要运行了
            if(_task_queue.empty() && !_isrunning)
            {
                UnlockQueue();
                std::cout<<name<<"quit..."<<std::endl;
                sleep(1);
                break;
            }
            //不论线程池有没有退出,走到这说明一定有任务 ->处理任务
            T t = _task_queue.front();
	        _task_queue.pop();
	        UnlockQueue();//解锁
            t();
        }
    }

 注意:这个任务是属于线程独占的任务,不能再任务队列的加锁、解锁之间处理

 入任务队列

如果线程阻塞等待的数量大于0,就唤醒一个线程

 

    bool Enqueue(const T &t)
    {
        bool ret = false;
        LockQueue();
        if(_isrunning)
        {
            _task_queue.push(t);
            if(_waitnum>0)
            {
                ThreadWakeup();
            }
            ret = true;
        }
        UnlockQueue();
        return ret;
    }

threadpool.hpp

任务还没写,所以t()先注释掉

#include<iostream>
#include<vector>
#include<unistd.h>
#include<string>
#include<queue>
#include"LockGuard.hpp"
#include"Thread.hpp"

using namespace ThreadModule;
const int g_thread_num = 3;//默认线程数
// 线程池->一批线程,一批任务,有任务push、有任务pop,本质是: 生产消费模型
template <typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadWakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }
public:
    ThreadPool(int threadnum=g_thread_num)//构造函数
        :_threadnum(threadnum)
        , _waitnum(0)
        , _isrunning(false)
    {
        pthread_mutex_init(&_mutex,nullptr);//初始化锁
        pthread_cond_init(&_cond,nullptr);//初始化条件变量
    }
    // static void Print()
    // {
    //     while(true)
    //     {
    //         std::cout<<"我是一个线程"<<std::endl;
    //         sleep(1);
    //     }
    // }
    // void Print(std::string name)
    // {
    //     while(true)
    //     {
    //         std::cout<<"我是一个线程,线程名是"<<name<<std::endl;
    //         sleep(1);
    //     }
    // }
    void InitThreadPool()
	{
	    // 指向构建出所有的线程,并不启动
        for (int num = 0; num < _threadnum; num++)
	    {
	        std::string name = "thread-" + std::to_string(num + 1);
            //_threads.emplace_back(Print,name);//线程处理函数是Print
            //_threads.emplace_back(std::bind(&ThreadPool::Print,this,std::placeholders::_1),name);
	        _threads.emplace_back(std::bind(&ThreadPool::HandlerTask,this,std::placeholders::_1),name);
	    }
	    _isrunning = true;
	}
    void Start()//启动线程池
    {
        for(auto &thread:_threads)
        {
            thread.Start();
            std::cout<<thread.name()<<"线程:启动成功"<<std::endl;
        }
    }

    void HandlerTask(std::string name)//线程处理任务
    {
        while (true)
        {
            //加锁
            LockQueue();
            //任务队列中不一定有数据,如果任务队列为空且线程池在跑,那么就阻塞住
            while(_task_queue.empty()&&_isrunning)
            {
                _waitnum++;
                std::cout<<name<<"线程阻塞中..."<<std::endl;
                ThreadSleep();
                _waitnum--;
            }
            //如果任务队列是空的,然后线程池又退出了,那么就没必要运行了
            if(_task_queue.empty() && !_isrunning)
            {
                UnlockQueue();
                std::cout<<name<<"quit..."<<std::endl;
                sleep(1);
                break;
            }
            //不论线程池有没有退出,走到这说明一定有任务 ->处理任务
            T t = _task_queue.front();
	        _task_queue.pop();
	        UnlockQueue();//解锁
            //t();
        }
    }

    void Stop()
	{
	    LockQueue();
	    _isrunning = false;
	    ThreadWakeupAll();
	    UnlockQueue();        
	}


    void Wait()
    {
        for(auto &thread:_threads)
        {
            thread.Join();
        }
    }

    bool Enqueue(const T &t)
    {
        bool ret = false;
        LockQueue();
        if(_isrunning)
        {
            _task_queue.push(t);
            if(_waitnum>0)
            {
                ThreadWakeup();
            }
            ret = true;
        }
        UnlockQueue();
        return ret;
    }


    ~ThreadPool()//析构
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    int _threadnum;//线程的数量
    std::vector<Thread> _threads;//用vector来存线程
    std::queue<T> _task_queue;//任务队列

    pthread_mutex_t _mutex;//锁
	pthread_cond_t _cond;//条件变量
	int _waitnum;
    bool _isrunning;//判断线程池是否在运行
};

 main.cc

#include <iostream>	
#include <string>
#include <memory>
#include "Task.hpp"
#include "threadpool.hpp"	
	
int main()
{
	std::unique_ptr<ThreadPool<int>> tp(new ThreadPool<int>()); 
	tp->InitThreadPool();
	tp->Start();
	sleep(2);

	tp->Stop();
    tp->Wait();
    return 0;
}

 

2.加上日志与任务

 LOG.hpp(日志)

#pragma once
#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include"LockGuard.hpp"
bool gIsSave = false;
const std::string logname = "log.txt";

// 1. 日志是有等级的
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
void SaveFile(const std::string &filename, const std::string &message)
{
    std::ofstream out(filename, std::ios::app);
    if (!out.is_open())
    {
        return;
    }
    out << message;
    out.close();
}
std::string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "Debug";
    case INFO:
        return "Info";
    case WARNING:
        return "Warning";
    case ERROR:
        return "Error";
    case FATAL:
        return "Fatal";
    default:
        return "Unknown";
    }
}

std::string GetTimeString()
{
    time_t curr_time = time(nullptr);
    struct tm *format_time = localtime(&curr_time);
    if (format_time == nullptr)
        return "None";
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return time_buffer;
}

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// 日志是有格式的
// 日志等级 时间 代码所在的文件名/行数 日志的内容
void LogMessage(std::string filename, int line,bool issave,int level, const char *format, ...)
{

    std::string levelstr = LevelToString(level);
    std::string timestr = GetTimeString();
    pid_t selfid = getpid();

    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, sizeof(buffer), format, arg);
    va_end(arg);

    std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +
                          "[" + std::to_string(selfid) + "]" +
                          "[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer + "\n";
    LockGuard lockguard(&lock);
    if (!issave)
    {
        std::cout << message;
    }
    else
    {
        SaveFile(logname, message);
    }
}
#define LOG(level, format, ...)                                                \
    do                                                                         \
    {                                                                          \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)

 LockGuard.hpp

#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__

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

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *mutex):_mutex(mutex)
    {
        pthread_mutex_lock(_mutex); // 构造加锁
    }
    ~LockGuard()
    {
        pthread_mutex_unlock(_mutex);
    }
private:
    pthread_mutex_t *_mutex;
};

#endif

threadpool.hpp

#include<iostream>
#include<vector>
#include<unistd.h>
#include<string>
#include<queue>	
#include"LOG.hpp"
#include"LockGuard.hpp"
#include"Thread.hpp"

using namespace ThreadModule;
const int g_thread_num = 3;//默认线程数
// 线程池->一批线程,一批任务,有任务push、有任务pop,本质是: 生产消费模型
template <typename T>
class ThreadPool
{
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void ThreadWakeupAll()
    {
        pthread_cond_broadcast(&_cond);
    }
public:
    ThreadPool(int threadnum=g_thread_num)//构造函数
        :_threadnum(threadnum)
        , _waitnum(0)
        , _isrunning(false)
    {
        pthread_mutex_init(&_mutex,nullptr);//初始化锁
        pthread_cond_init(&_cond,nullptr);//初始化条件变量
        LOG(INFO, "线程池构造成功");
    }
    // static void Print()
    // {
    //     while(true)
    //     {
    //         std::cout<<"我是一个线程"<<std::endl;
    //         sleep(1);
    //     }
    // }
    // void Print(std::string name)
    // {
    //     while(true)
    //     {
    //         std::cout<<"我是一个线程,线程名是"<<name<<std::endl;
    //         sleep(1);
    //     }
    // }
    void InitThreadPool()
	{
	    // 指向构建出所有的线程,并不启动
        for (int num = 0; num < _threadnum; num++)
	    {
	        std::string name = "thread-" + std::to_string(num + 1);
            //_threads.emplace_back(Print,name);//线程处理函数是Print
            //_threads.emplace_back(std::bind(&ThreadPool::Print,this,std::placeholders::_1),name);
	        _threads.emplace_back(std::bind(&ThreadPool::HandlerTask,this,std::placeholders::_1),name);
            LOG(INFO, "线程 %s 初始化成功", name.c_str());
	    }
	    _isrunning = true;
	}
    void Start()//启动线程池
    {
        for(auto &thread:_threads)
        {
            thread.Start();
            std::cout<<thread.name()<<"线程:启动成功"<<std::endl;
        }
    }

    void HandlerTask(std::string name)//线程处理任务
    {
        LOG(INFO, "%s 正在运行...", name.c_str());
        while (true)
        {
            //加锁
            LockQueue();
            //任务队列中不一定有数据,如果任务队列为空且线程池在跑,那么就阻塞住
            while(_task_queue.empty()&&_isrunning)
            {
                _waitnum++;
                ThreadSleep();
                _waitnum--;
            }
            //如果任务队列是空的,然后线程池又退出了,那么就没必要运行了
            if(_task_queue.empty() && !_isrunning)
            {
                UnlockQueue();
                //std::cout<<name<<"quit..."<<std::endl;
                sleep(1);
                break;
            }
            //不论线程池有没有退出,走到这说明一定有任务 ->处理任务
            T t = _task_queue.front();
	        _task_queue.pop();
	        UnlockQueue();//解锁
            LOG(DEBUG, "%s 获得任务", name.c_str());
            t();
            LOG(DEBUG,"%s 处理任务中,结果是%s",name.c_str(), t.ResultToString().c_str());
        }
    }

    void Stop()
	{
	    LockQueue();
	    _isrunning = false;
	    ThreadWakeupAll();
	    UnlockQueue();        
	}


    void Wait()
    {
        for(auto &thread:_threads)
        {
            thread.Join();
            LOG(INFO, "%s 线程退出...", thread.name().c_str());
        }
    }

    bool Enqueue(const T &t)
    {
        bool ret = false;
        LockQueue();
        if(_isrunning)
        {
            _task_queue.push(t);
            if(_waitnum>0)
            {
                ThreadWakeup();
            }
            LOG(DEBUG, "任务入队列成功");
            ret = true;
        }
        UnlockQueue();
        return ret;
    }


    ~ThreadPool()//析构
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    int _threadnum;//线程的数量
    std::vector<Thread> _threads;//用vector来存线程
    std::queue<T> _task_queue;//任务队列

    pthread_mutex_t _mutex;//锁
	pthread_cond_t _cond;//条件变量
	int _waitnum;
    bool _isrunning;//判断线程池是否在运行
};

thread.hpp

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

namespace ThreadModule
{
    using func_t = std::function<void(std::string)>;

    class Thread
    {
    public:
        void Excute()
        {
            _func(_threadname);
        }
    public:
        Thread(func_t func, std::string name="none-name")
            : _func(func), _threadname(name), _stop(true)
        {}
        static void *threadroutine(void *args) // 类成员函数,形参是有this指针的!!
        {
            Thread *self = static_cast<Thread *>(args);
            self->Excute();
            return nullptr;
        }
        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, threadroutine, this);
            if(!n)
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }
        void Detach()
        {
            if(!_stop)
            {
                pthread_detach(_tid);
            }
        }
        void Join()
        {
            if(!_stop)
            {
                pthread_join(_tid, nullptr);
            }
        }
        std::string name()
        {
            return _threadname;
        }
        void Stop()
        {
            _stop = true;
        }
        ~Thread() {}

    private:
        pthread_t _tid;//线程tid
        std::string _threadname;//线程名字
        func_t _func;//线程所要执行的函数
        bool _stop;//判断线程是否停止
    };
}

 

 main.cc

#include <iostream>	
#include <string>
#include <memory>
#include "LOG.hpp"
#include "threadpool.hpp"	
#include "Task.hpp"	
#include<ctime>

        
int main()
{
	srand(time(nullptr) ^ getpid() ^ pthread_self());
	std::unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>(5)); 
	tp->InitThreadPool();
	tp->Start();
	
	int tasknum=3;
	while(tasknum)
	{
		int a = rand() % 12 + 1;
		usleep(1000);
		int b = rand() % 4 + 1;
		Task t(a, b);
		LOG(INFO, "主线程推送任务: %s", t.DebugToString().c_str());
		tp->Enqueue(t);
		sleep(1);
		tasknum--;
	}

	tp->Stop();
    tp->Wait();
    return 0;
}

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

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

相关文章

九、Linux二进制安装ElasticSearch集群

目录 九、Linux二进制安装ElasticSearch集群1 下载2 安装前准备(单机&#xff0c;集群每台机器都需要配置)3 ElasticSearch单机&#xff08;7.16.2&#xff09;4 ElasticSearch集群&#xff08;8.14.2&#xff09;4.1 解压文件&#xff08;先将下载文件放到/opt下&#xff09;4…

Java系列-valitile

背景 volatile这个关键字可以说是面试过程中出现频率最高的一个知识点了&#xff0c;面试官的问题也是五花八门&#xff0c;各种刁钻的角度。之前也是简单背过几道八股文&#xff0c;什么可见性&#xff0c;防止指令重拍等&#xff0c;但面试官一句&#xff1a;volatile原理是什…

Vue基础--v-model/v-for/事件属性/侦听器

目录 一 v-model表单元素 1.1 v-model绑定文本域的value 1.1.1 lazy属性&#xff1a;光标离开再发请求 1.1.2 number属性&#xff1a;如果能转成number就会转成numer类型 1.1.3 trim属性&#xff1a;去文本域输入的前后空格 1.2v-model绑定单选checkbox 1.3代码展示 二 …

Python OpenCV 教学取得视频资讯

这篇教学会介绍使用OpenCV&#xff0c;取得影像的长宽尺寸、以及读取影像中某些像素的颜色数值。 因为程式中的OpenCV 会需要使用镜头或GPU&#xff0c;所以请使用本机环境( 参考&#xff1a;使用Python 虚拟环境) 或使用Anaconda Jupyter 进行实作( 参考&#xff1a;使用Anaco…

基于单片机的温湿度感应智能晾衣杆系统设计

&#xff3b;摘 要&#xff3d; 本设计拟开发一种湿度感应智能晾衣杆系统 &#xff0c; 此新型晾衣杆是以单片机为主控芯片 来控制的实时检测系统 &#xff0e; 该系统使用 DHT11 温湿度传感器来检测大气的温湿度 &#xff0c; 然后通过单 片机处理信息来控制 28BYJ &…

配置路由器支持Telnet操作 计网实验

实验要求&#xff1a; 假设某学校的网络管理员第一次在设备机房对路由器进行了初次配置后&#xff0c;他希望以后在办公室或出差时也可以对设备进行远程管理&#xff0c;现要在路由器上做适当配置&#xff0c;使他可以实现这一愿望。 本实验以一台R2624路由器为例&#xff0c;…

使用 Hugging Face 的 Transformers 库加载预训练模型遇到的问题

题意&#xff1a; Size mismatch for embed_out.weight: copying a param with shape torch.Size([0]) from checkpoint - Huggingface PyTorch 这个错误信息 "Size mismatch for embed_out.weight: copying a param with shape torch.Size([0]) from checkpoint - Hugg…

Redis管理禁用命令

在redis数据量比较大时&#xff0c;执行 keys * &#xff0c;fluashdb 这些命令&#xff0c;会导致redis长时间阻塞&#xff0c;大量请求被阻塞&#xff0c;cpu飙升&#xff0c;严重可能导致redis宕机&#xff0c;数据库雪崩。所以一些命令在生产环境禁止使用。 Redis 禁用命令…

开始尝试从0写一个项目--前端(二)

修改请求路径的位置 将后续以及之前的所有请求全都放在同一个文件夹里面 定义axios全局拦截器 为了后端每次请求都需要向后端传递jwt令牌检验 ps&#xff1a;愁死了&#xff0c;翻阅各种资料&#xff0c;可算是搞定了&#xff0c;哭死~~ src\utils\request.js import axio…

【QML之·基础语法概述】

系列文章目录 文章目录 前言一、QML基础语法二、属性三、脚本四、核心元素类型4.1 元素可以分为视觉元素和非视觉元素。4.2 Item4.2.1 几何属性(Geometry&#xff09;:4.2.2 布局处理:4.2.3 键处理&#xff1a;4.2.4 变换4.2.5 视觉4.2.6 状态定义 4.3 Rectangle4.3.1 颜色 4.4…

互联网3.0时代的变革者:华贝甄选大模型创新之道

在当今竞争激烈的商业世界中&#xff0c;华贝甄选犹如一颗璀璨的明星&#xff0c;闪耀着独特的光芒。 华贝甄选始终将技术创新与研发视为发展的核心驱动力。拥有先进的研发团队和一流设施&#xff0c;积极探索人工智能、大数据、区块链等前沿技术&#xff0c;为用户提供高性能…

Knife4j的介绍与使用

目录 一、简单介绍1.1 简介1.2 主要特点和功能&#xff1a; 二、使用步骤&#xff1a;2.1 添加依赖&#xff1a;2.2 yml数据源配置2.3 创建knife4j配置类2.4 注解的作用 最后 一、简单介绍 1.1 简介 Knife4j 是一款基于Swagger的开源文档管理工具&#xff0c;主要用于生成和管…

【PTA天梯赛】L1-003 个位数统计(15分)

作者&#xff1a;指针不指南吗 专栏&#xff1a;算法刷题 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录 题目题解总结 题目 题目链接 题解 使用string把长度达1000位的数字存起来开一个代表个位数的数组 a[11]倒序计算最后一位&#xff0c;…

第16章 主成分分析:四个案例及课后习题

1.假设 x x x为 m m m 维随机变量&#xff0c;其均值为 μ \mu μ&#xff0c;协方差矩阵为 Σ \Sigma Σ。 考虑由 m m m维随机变量 x x x到 m m m维随机变量 y y y的线性变换 y i α i T x ∑ k 1 m α k i x k , i 1 , 2 , ⋯ , m y _ { i } \alpha _ { i } ^ { T } …

从微软 Word 中提取数据

从 Microsoft Word 文档中提取数据可以通过编程来实现&#xff0c;有几种常见的方法&#xff0c;其中之一是使用 Python 和 python-docx 库。python-docx 是一个处理 .docx 文件&#xff08;Microsoft Word 文档&#xff09;的 Python 库&#xff0c;可以读取和操作 Word 文档的…

泛微开发修炼之旅--36通过js控制明细表中同一列中多个浏览框的显示控制逻辑(明细表列中多字段显示逻辑控制)

文章链接&#xff1a;36通过js控制明细表中同一列中多个浏览框的显示控制逻辑&#xff08;明细表列中多字段显示逻辑控制&#xff09;

谷粒商城学习笔记-22-分布式组件-SpringCloud-OpenFeign测试远程调用

文章目录 一&#xff0c;OpenFeign的简介二&#xff0c;OpenFeign的使用步骤1&#xff0c;场景说明2&#xff0c;引入依赖2&#xff0c;开启OpenFeign3&#xff0c;编写Feign接口4&#xff0c;使用feign调用远程接口5&#xff0c;验证 错误记录 上一节学习了注册中心&#xff0…

变长输入神经网络设计

我对使用 PyTorch 可以轻松构建动态神经网络的想法很感兴趣&#xff0c;因此我决定尝试一下。 我脑海中的应用程序具有可变数量的相同类型的输入。对于可变数量的输入&#xff0c;已经使用了循环或递归神经网络。但是&#xff0c;这些结构在给定行的输入之间施加了一些顺序或层…

前端面试题31(TCP与UDP区别)

TCP (Transmission Control Protocol) 和 UDP (User Datagram Protocol) 是两种在网络通信中常用的传输层协议&#xff0c;它们在多个方面存在显著差异&#xff0c;主要体现在以下几个方面&#xff1a; 连接方式&#xff1a; TCP 是面向连接的协议。在数据传输开始之前&#xf…

STM32学习历程(day6)

EXTI外部中断使用教程 首先先看下EXTI的框图 看这个框图就能知道要先初始化GPIO外设 那么和前面一样 1、先RCC使能时钟 2、配置GPIO 选择端口为输入模式&#xff0c; 3、配置AFIO&#xff0c;选择我们用的GPIO连接到后面的EXTI 4、配置EXTI&#xff0c;选择边沿触发方式…