线程池c代码实现

news2024/11/16 9:17:46

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、线程池是什么?
  • 二、代码示例
  • 总结


前言

线程池c代码简单实现:
大致思路如下:
一个管理线程轮询工作线程是否空闲,空闲的话从工作队列中取出work函数给工作线程处理
在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、线程池是什么?

线程池是一种用于管理一组预先创建的线程的技术,它能够高效地处理大量并发任务。线程池的核心思想是复用线程,而不是为每个任务都创建和销毁线程,这样可以显著减少线程创建和销毁所带来的开销,并提高系统的整体性能。

线程池的优点
1,减少线程创建和销毁的开销:线程的创建和销毁是一个相对耗时的过程,线程池通过复用线程来避免频繁的创建和销毁,提高了效率。
2,控制并发度:线程池可以限制同时运行的线程数量,从而控制系统的并发度,防止过度消耗系统资源。
3,提高响应速度:由于线程已经在池中预先创建,当新任务到来时,可以直接从池中获取线程执行,无需等待线程创建过程。
4,资源管理:线程池可以更有效地管理资源,比如限制最大线程数量,避免系统资源耗尽。

线程池的基本组成部分
1,线程池:包含一组预先创建的线程。
2,任务队列:用于保存待处理的任务。
3,任务调度器:负责将任务分发给空闲的线程执行。
4,线程工厂:用于创建新线程(虽然线程池通常会复用线程,但在某些情况下可能需要创建新线程)。
线程池的工作流程
1,任务提交:当一个任务需要被执行时,它会被提交给线程池。
2,任务分配:如果线程池中有空闲线程,那么任务会被分配给其中一个线程执行。
3,任务执行:线程开始执行任务。
4,任务完成:任务完成后,线程回到线程池中等待分配新的任务。

二、代码示例

thread_pool.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "double_list.h"


#define CONTAINER_OF(ptr, type, member) \
    ((type *)((char *)(ptr) - offsetof(type, member)))

/*
// 任务数据结构
typedef struct {
    int id;                   //最好能传递 提交任务的线程pid
    void* (*function)(void *); // 指向任务函数的指针
    void *arg;                // 传递给任务函数的参数
} task_t;
*/
typedef struct {
    DoublyLinkedList list;
    int stack_size;
    pthread_t *thread_id; //工作线程的 thread id
    task_t *thread_arg; //工作线程的 输入arg
    int thread_num;
    pthread_t thread_mg; //管理线程

    int thread_pool_pause;
    int thread_pool_stop;

    void* ext_arg;
} thread_pool_t;

typedef struct {
    thread_pool_t * tp;
    int num;
} work_th_arg;

// 线程池工作线程的主循环
void *thread_pool_thread(void *arg) {
    thread_pool_t *tp = ((work_th_arg *)arg)->tp;
    int thread_num = ((work_th_arg *)arg)->num;
    int ret = 0;

    printf("tp = %x, thread_num = %d\n", tp, thread_num);

    tp->thread_arg[thread_num].id = -1;
    struct timespec ts, rem;
    // 设置要睡眠的时间为5毫秒
    ts.tv_sec = 0; // 秒
    ts.tv_nsec = 5 * 1000 * 1000; // 5毫秒转换为纳秒

    free(arg);
    
    while (1) {
        // 如果线程池关闭并且队列为空,则退出线程
        if(tp->thread_pool_stop == 1 || tp->thread_arg == NULL) {
            pthread_exit(NULL);
        }

        // 如果队列为空则等待
        if(tp->thread_pool_pause == 1 || tp->thread_arg[thread_num].id == -1) {
            nanosleep(&ts, &rem); // 睡眠5ms
            continue;
        }

        // 获取任务

        // 执行任务
        ret = tp->thread_arg[thread_num].function(tp->thread_arg[thread_num].arg);
        tp->thread_arg[thread_num].id = -1;
        printf("thread_num = %d, do task done ret = %d\n", thread_num, ret);
    }
}


// 线程池 管理线程的主循环
void *thread_pool_manage(void *arg) {
    thread_pool_t *tp = (thread_pool_t *)arg;
    task_t tmp_ta = {0};
    int ret = 0;

    struct timespec ts, rem;
    // 设置要睡眠的时间为2毫秒
    ts.tv_sec = 0; // 秒
    ts.tv_nsec = 2 * 1000 * 1000; // 5毫秒转换为纳秒

    printf("thread_pool_manage enter\n");

    while (1) {
        // 如果线程池关闭并且队列为空,则退出线程
        if(tp->thread_pool_stop == 1 || tp->thread_arg == NULL) {
            pthread_exit(NULL);
        }

        // 如果队列为空则等待
        if(tp->thread_pool_pause == 1 || is_list_empty(&(tp->list))) {
            //printf("nanosleep enter\n");
            nanosleep(&ts, &rem); // 睡眠5ms
            continue;
        }

        for(int i = 0; i < tp->thread_num; i++)
        {
            if(tp->thread_arg[i].id == -1)
            {
              printf("queue_dequeue enter\n");
               ret = queue_dequeue(&(tp->list),&tmp_ta);
               if(ret != 0)
               {
                   break;
               }
               tp->thread_arg[i].function = tmp_ta.function;
               tp->thread_arg[i].arg = tmp_ta.arg;
               tp->thread_arg[i].id = tmp_ta.id;
            }
        }

    }
}


int thread_pool_init(thread_pool_t *tp,int thread_num, int stack_size)
{
    int ret = 0;
    
    if(tp == NULL)
    {
        return -1;
    }
    
    if(thread_num == 0)
    {
        tp->thread_num = 100;
    }else
    {
        tp->thread_num = thread_num;
    }
    
    if(stack_size == 0)
    {
        tp->stack_size = 1000;
    }else
    {
        tp->stack_size = stack_size;
    }

    initDoublyList(&(tp->list));

    list_set_maxlen(&(tp->list), tp->stack_size);

    tp->thread_arg = malloc(tp->thread_num * sizeof(task_t));
    if(tp->thread_arg == NULL)
    {
        return -1;
    }

    tp->thread_id = malloc(tp->thread_num * sizeof(pthread_t));
    if(tp->thread_arg == NULL)
    {
        ret = -1;
        goto error;
    }

    tp->thread_pool_pause = 1;
    tp->thread_pool_stop = 0;

    for(int i = 0; i < tp->thread_num; i++)
    {   
        work_th_arg *th_arg = malloc(sizeof(work_th_arg));
        if(th_arg == NULL)
        {
            return -1;
        }
        tp->thread_arg[i].id = -1;
        
        th_arg->tp = tp;
        th_arg->num = i;
        
        if (pthread_create(&(tp->thread_id[i]), NULL, thread_pool_thread, (void*)th_arg) != 0) {
            perror("Failed to create thread 1");
            ret = -1;
            free(th_arg);
            goto error;
        }
        //printf("tp->thread_id[%d] = %d\n", i , tp->thread_id[i]);
    }
    
    if (pthread_create(&(tp->thread_mg), NULL, thread_pool_manage, (void*)tp) != 0) {
        perror("Failed to create thread 1");
        ret = -1;
        goto error;
    }

    return 0;

error:
    if(tp->thread_arg)
        free(tp->thread_arg);
    if(tp->thread_id)
        free(tp->thread_id);

    return ret;
}


int thread_pool_stop(thread_pool_t *tp)
{
    if(NULL == tp)
    {
        return -1;
    }
    
    tp->thread_pool_stop = 1;
    
    for(int i = 0; i < tp->thread_num; i++)
    {
        //printf("thread_pool_stop tp->thread_id[%d] = %d\n", i , tp->thread_id[i]);
        pthread_join(tp->thread_id[i], NULL);
    }

    pthread_join(tp->thread_mg, NULL);

    return 0;
}

int thread_pool_run(thread_pool_t *tp, int pause)
{
    if(NULL == tp)
    {
        return -1;
    }
    
    tp->thread_pool_pause = pause;

    return 0;
}



int thread_pool_uninit(thread_pool_t *tp)
{
    int ret = 0;
    
    if(tp == NULL)
    {
        return -1;
    }

    thread_pool_stop(tp);

    clearDoublyList(&(tp->list));

    if(tp->thread_arg)
        free(tp->thread_arg);
    if(tp->thread_id)
        free(tp->thread_id);


    tp->thread_pool_pause = 0;
    tp->thread_pool_stop = 0;
    tp->thread_mg = 0;

error:
    return ret;
}



int task_fun(int arg)
{
    printf("task_fun enter\n");
    for(int i = 0; i < arg; i++)
    {
        for(int j = 0; j < 1000; j++)
        {
            asm("nop");
        }
    }

    sleep(arg);
    
    return arg;
}



int main()
{
    task_t task_test = {0};
    thread_pool_t *tp = malloc(sizeof(thread_pool_t));
    thread_pool_init(tp,  10, 100);

    //run
    thread_pool_run(tp, 0);

    for(int i = 0; i < 20; i++)
    {
        task_test.id = i;
        task_test.function = task_fun;
        task_test.arg = i;
        queue_enqueue(&(tp->list), task_test);
    }

    usleep(30* 1000 * 1000);
    
    thread_pool_uninit(tp);
    free(tp);
}

double_list.h

#ifndef _DOUBLE_LIST_H_
#define _DOUBLE_LIST_H_

#define USE_MUTEX

#define MYTYPE task_t

//special struct define
// 任务数据结构
typedef struct {
    int id;                   //最好能传递 提交任务的线程pid
    void* (*function)(void *); // 指向任务函数的指针
    void *arg;                // 传递给任务函数的参数
} task_t;


typedef struct DoublyListNode {
    MYTYPE data;
    struct DoublyListNode* prev;
    struct DoublyListNode* next;
} DoublyListNode;

typedef struct DoublyLinkedList {
    DoublyListNode* head;
    DoublyListNode* tail;
#ifdef USE_MUTEX
    pthread_mutex_t lock; // 添加互斥锁
#endif
    int list_len_max;
    int list_len_cur;
} DoublyLinkedList;



void initDoublyList(DoublyLinkedList* list);
void clearDoublyList(DoublyLinkedList* list);
void printDoublyList(DoublyLinkedList* list);

void list_set_maxlen(DoublyLinkedList* list,int len);

int insertAtHead(DoublyLinkedList *list, MYTYPE value);
int insertAtTail(DoublyLinkedList *list, MYTYPE value);
int deleteAtHead(DoublyLinkedList *list, MYTYPE *value);
int deleteAtTail(DoublyLinkedList *list, MYTYPE *value);


//full/enpty
int is_list_empty(DoublyLinkedList *list);
int is_list_full(DoublyLinkedList *list);

//queue
int queue_enqueue(DoublyLinkedList *list, MYTYPE value);
int queue_dequeue(DoublyLinkedList *list, MYTYPE *value);



//stack
int stack_push(DoublyLinkedList *list, MYTYPE value);
int stack_pop(DoublyLinkedList *list, MYTYPE *value);


#endif

double_list.c

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include "double_list.h"
#include <string.h>

void list_set_maxlen(DoublyLinkedList* list, int len)
{
    if(NULL == list)
    {
        printf("invalid parameter\n");
        return;
    }
    list->list_len_max = len;
}

void initDoublyList(DoublyLinkedList* list) {
    list->head = NULL;
    list->tail = NULL;
#ifdef USE_MUTEX
    pthread_mutex_init(&list->lock, NULL); // 初始化互斥锁
#endif
    list->list_len_max = -1;
    list->list_len_cur = 0;
}

//清空链表
void clearDoublyList(DoublyLinkedList* list) {
    DoublyListNode* current = list->head;
    while (current != NULL) {
        DoublyListNode* next = current->next;
        //printf("clearDoublyList free\n");
        free(current);
        current = next;
    }
    list->head = NULL;
    list->tail = NULL;
    list->list_len_cur = 0;
    list->list_len_max = -1;
#ifdef USE_MUTEX
    pthread_mutex_destroy(&list->lock);
#endif
}

//遍历并打印链表
void printDoublyList(DoublyLinkedList* list) {
#ifdef USE_MUTEX
    pthread_mutex_lock(&list->lock);
#endif
    DoublyListNode* current = list->head;
    while (current != NULL) {
        printf("%d <-> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
#ifdef USE_MUTEX   
    pthread_mutex_unlock(&list->lock);
#endif   
}

//向链表头部插入元素
int insertAtHead(DoublyLinkedList *list, MYTYPE value) {

    int ret = 0;
#ifdef USE_MUTEX    
    pthread_mutex_lock(&list->lock);
#endif

    if(list == NULL || (list->list_len_max != -1 && list->list_len_cur >= list->list_len_max))
    {
        printf("invalid parameter or list full\n");
        ret = -1;
        goto error;
    }

    DoublyListNode *newNode = (DoublyListNode *)malloc(sizeof(DoublyListNode));
    if (newNode == NULL) {
        printf("Memory allocation failed\n");
        ret = -1;
        goto error;
    }

    memcpy(&(newNode->data), &value, sizeof(MYTYPE));
    newNode->prev = NULL;
    newNode->next = list->head;

    if (list->head != NULL)
        list->head->prev = newNode;
    else
        list->tail = newNode;

    list->head = newNode;
    
    list->list_len_cur++;

error:
#ifdef USE_MUTEX
    pthread_mutex_unlock(&list->lock);
#endif
    return 0;
}

//向链表尾部插入元素
int insertAtTail(DoublyLinkedList *list, MYTYPE value) {
    int ret = 0;
#ifdef USE_MUTEX    
    pthread_mutex_lock(&list->lock);
#endif

    if(list == NULL || (list->list_len_max != -1 && list->list_len_cur >= list->list_len_max))
    {
        printf("invalid parameter or list full\n");
        ret = -1;
        goto error;
    }

    DoublyListNode *newNode = (DoublyListNode *)malloc(sizeof(DoublyListNode));
    if (newNode == NULL) {
        printf("Memory allocation failed\n");
        ret = -1;
        goto error;
    }
   
    memcpy(&(newNode->data), &value, sizeof(MYTYPE));
    newNode->next = NULL;
    newNode->prev = list->tail;

    if (list->tail != NULL)
        list->tail->next = newNode;
    else
        list->head = newNode;

    list->tail = newNode;

    list->list_len_cur++;

error:
#ifdef USE_MUTEX
    pthread_mutex_unlock(&list->lock);
#endif
    return 0;
}


//从链表头部删除元素
int deleteAtHead(DoublyLinkedList *list, MYTYPE * value) {

    int ret = 0;
    
    if(list == NULL || value == NULL)
    {
        printf("invalid parameter\n");
        return -1;
    }
    
#ifdef USE_MUTEX
    pthread_mutex_lock(&list->lock);
#endif

    if (list->head == NULL) {
        printf("List is empty\n");
        list->tail = NULL;
        ret = -1;
        goto error;
    }
    
    // 头和尾指针指向一个node,只有一个元素
    if(list->head == list->tail)
    {
        list->tail = NULL;
        list->head->next = NULL;
    }
    
    DoublyListNode *nodeToDelete = list->head;
    memcpy(value, &(nodeToDelete->data), sizeof(MYTYPE));
    list->head = nodeToDelete->next;

    if (list->head != NULL)
        list->head->prev = NULL;
//    else
//        list->tail = NULL;

    //printf("deleteAtHead free\n");
    free(nodeToDelete);

    list->list_len_cur--;

error:
#ifdef USE_MUTEX
    pthread_mutex_unlock(&list->lock);
#endif
    return 0;
}

//从链表末尾删除元素
int deleteAtTail(DoublyLinkedList *list, MYTYPE * value) {

    int ret = 0;
    
    if(list == NULL || value == NULL)
    {
        printf("invalid parameter\n");
        return -1;
    }
    
#ifdef USE_MUTEX
    pthread_mutex_lock(&list->lock);
#endif

    if (list->tail == NULL) {
        printf("List is empty\n");
        list->head = NULL;
        ret = -1;
        goto error;
    }

    // 头和尾指针指向一个node,只有一个元素
    if(list->head == list->tail)
    {
        list->head = NULL;
        list->tail->prev = NULL;
    }

    DoublyListNode *nodeToDelete = list->tail;
    memcpy(value, &(nodeToDelete->data), sizeof(MYTYPE));
    list->tail = nodeToDelete->prev;

    if (list->tail != NULL)
        list->tail->next = NULL;
//    else
//        list->head = NULL;

    //printf("deleteAtTail free\n");
    free(nodeToDelete);

    list->list_len_cur--;

error:
#ifdef USE_MUTEX
    pthread_mutex_unlock(&list->lock);
#endif
    return ret;
}



int is_list_full(DoublyLinkedList *list)
{
    int ret = 0;
#ifdef USE_MUTEX    
    pthread_mutex_lock(&list->lock);
#endif

    if(list->list_len_max != -1 && list->list_len_cur >= list->list_len_max)
    {
        ret = 1;
    }

#ifdef USE_MUTEX    
    pthread_mutex_unlock(&list->lock);
#endif

    return ret;
}

int is_list_empty(DoublyLinkedList *list)
{
    int ret = 0;
#ifdef USE_MUTEX    
    pthread_mutex_lock(&list->lock);
#endif

    if(list->tail == NULL && list->head == NULL && list->list_len_cur == 0)
    {
        ret = 1;
    }

#ifdef USE_MUTEX    
    pthread_mutex_unlock(&list->lock);
#endif

    return ret;
}


// queue
int queue_enqueue(DoublyLinkedList *list, MYTYPE value)
{
    return insertAtTail(list, value);
}

int queue_dequeue(DoublyLinkedList *list, MYTYPE *value)
{
    return deleteAtHead(list, value);
}



//stack
int stack_push(DoublyLinkedList *list, MYTYPE value)
{
     return insertAtHead(list, value);
}

int stack_pop(DoublyLinkedList *list, MYTYPE *value)
{
    return deleteAtHead(list, value);
}




总结

线程池c代码实现,支持设置线程数和任务队列大小,可以运行仅供参考

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

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

相关文章

程序员如何在人工智能时代保持核心竞争力

目录 1.概述 1.1. 技术深度与广度的平衡 1.2. 软技能的培养 1.3. 持续学习和适应性 1.4. 理解和应用AI 1.5. 伦理和责任意识 2.AI辅助编程对程序员工作的影响 2.1.AI工具对编码实践的积极影响 2.2.AI工具的潜在风险 2.3.如何平衡利与弊 3.程序员应重点发展的核心能力…

RabbitMQ docker安装

后台配置文件 rabbitmq:image: rabbitmq:latestcontainer_name: rabbitmqports:- "5672:5672" # RabbitMQ server port- "15672:15672" # RabbitMQ management console portenvironment:RABBITMQ_DEFAULT_USER: adminRABBITMQ_DEFAULT_PASS: admin 若要打…

使用 Mojo 中的 Mandelbrot 和 Python 绘图

Mojo不仅非常适合编写高性能代码,而且还允许我们利用庞大的Python生态系统中的库和工具。通过无缝的Python互操作性,Mojo可以使用Python来做它擅长的事情,特别是gui,而不会牺牲关键代码的性能。让我们使用经典的Mandelbrot集合算法并在Mojo中实现它。 本教程展示了Mojo的两…

UE 选中框

【UE】框选功能_ue框选-CSDN博客 虚幻4 小功能教程系列-P33 覆盖物框选(框选场景里的物体)_哔哩哔哩_bilibili 步骤 先重载 UI中函数 OnPaint Position&#xff1a;起始位置 Size&#xff1a;大小 Brush: 选中框样式设置 在内容浏览器中新建一个“Slate笔刷” Tint&#x…

2023 江苏省第一届数据安全技术应用职业技能竞赛 决赛 部分wp

文章目录 一、前言比赛平台全貌题目附件及工具下载&#xff08;123网盘&#xff09; 二、参考文章三、题目&#xff08;解析&#xff09;一、内存取证-MemoryLife1、请给出内存镜像中黑客使用工具对外连接的IP地址及端口号是___________。&#xff08;格式为IP_PORT&#xff09…

Windows Server Backup(2016) 备份

Windows Server Backup(2016) 备份 1.使用 Windows Server Backup 备份 点击添加角色和功能&#xff0c;根据向导&#xff0c;添加 Windows Server Backup 功能。 添加完成后可通过 控制面板\系统和安全\管理工具 找到 Windows Server Backup&#xff0c;如下图 打开后如图&…

Openlayers6之地图覆盖物Overlay详解及使用,地图标注及弹窗查看详情(结合React)

demo案例&#xff1a;用户实现地图加载人员位置定位&#xff0c;并设置人员图片文字等标注&#xff0c;点击定位点查看人员详情。 主要通过ol/geom Point设置Style和ol/Overlay实现。主要实现步骤&#xff1a; 实现图文标注的实质是添加点时设置Ponit的样式&#xff0c;图片标…

浅谈安科瑞智慧用电系统在电气火灾中的应用

摘要&#xff1a;为了对电气火灾事故进行预测和预警&#xff0c;同时为了对电气火灾事故的应急救援提供 支持&#xff0c;将智慧用电监控系统应用于电气火灾中。该系统利用物联网、移动互联网、云平台、大数据技术&#xff0c;实现对电气线路电流、漏电、温度、谐波等参数进行…

leetcode日记(66)子集

实际上和上一题差不多&#xff0c;可以直接套用上一题回溯递归的函数写出来&#xff0c;复杂度比较高&#xff0c;因为是按照数字个数依次代入函数&#xff0c;然后通通放入一个vector中。 class Solution { public:vector<vector<int>> subsets(vector<int>…

Linux 实验基础环境准备(外网篇)

1.关闭禁用防火墙和selinux systemctl disable firewalld --now sed -i s/SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config2.保证可以连接外网 ping -c3 www.baidu.com 3.配置yum为阿里仓库并下载epel源 mkdir /etc/yum.repos.d/bak/; mv /etc/yum.repos.d/*.repo /e…

【AI-16】浅显易懂说一下RNN和Transformer

循环神经网络&#xff08;RNN&#xff09;曾经是自然语言处理领域的主流&#xff0c;但它们面临着长距离依赖和梯度消失等问题&#xff0c;限制了其在处理长文本序列时的表现。随后&#xff0c;Transformer模型的出现改变了这一局面。 循环神经网络&#xff08;RNN&#xff09…

实施MES管理系统的过程中可能会遇到的风险

在制造业的数字化转型浪潮中&#xff0c;MES管理系统的部署成为了企业提升生产效率、优化资源配置的关键一环。然而&#xff0c;这一过程的复杂性和潜在风险不容忽视。本文将从多个维度探讨实施MES管理系统的过程中可能面临的挑战&#xff0c;并提出一系列策略以应对这些挑战&a…

工作随记:我在OL8.8部署oracle rac遇到的问题

文章目录 一、安装篇问题1&#xff1a;[INS-08101] Unexpected error while executing the action at state:supportedosCheck问题1解决办法&#xff1a;问题2&#xff1a;[INS-06003] Failed to setup passwordless SSH connectivity with thefollowing nodeis): [xxxx1, xxxx…

天玑9400新猛料:CPU性能提升30%,同场景仅需8G3 30%功耗

年底的手机市场的新消息简直让人应接不暇&#xff0c;而其中最令人期待的&#xff0c;无疑是天玑9400旗舰芯。这款芯片据说性能提升了30%&#xff0c;在相同场景下功耗却降低到了8G3的30%。网友们纷纷表示&#xff1a;“发哥这次真的稳住了&#xff0c;天玑系列越来越给力&…

Python中的类多态之方法重写和动态绑定使用详解

概要 多态(Polymorphism)是面向对象编程的核心特性之一,它允许同一接口在不同的类中具有不同的实现。多态通过方法重写和动态绑定来实现,使得代码更加灵活和可扩展。本文将详细介绍Python中的类多态,包括方法重写和动态绑定,涵盖基本概念、具体用法和实际应用示例。 多态…

Linux磁盘管理与文件结构(一):磁盘、MBR与分区和文件系统

文章目录 1、磁盘结构物理结构数据结构硬盘存储容量数据区域定位磁盘接口类型 2、MBR与磁盘分区表示主引导记录&#xff08;MBR&#xff09;磁盘分区结构示例 磁盘分区表示 3、文件系统类型XFS 文件系统Swap 交换文件系统Linux 支持的其他文件系统类型 1、磁盘结构 物理结构 …

vue前端自适应布局,一步到位所有自适应

页面展示 实现内容 1&#xff0c;左右布局 左侧固定宽带&#xff0c;右侧自适应剩余的宽度。中间一条分割线&#xff0c;可以拖拉&#xff0c;自适应调整左右侧的宽度。左侧的高度超长自动出现横向滚动条&#xff0c;左侧宽度超长&#xff0c;自动出现竖向滚动条。 2&#x…

栈的实现及括号匹配问题

一、栈的概念及结构 栈是一种特殊的线性表&#xff0c;只允许在固定的一端进行插入删除元素操作。 进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵循后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&#xff1a…

Linux/C 高级——shell脚本

1. shell脚本基础概念 1.1概念 shell使用方式&#xff1a;手动下命令和脚本 脚本本质是一个文件&#xff0c;文件里面存放的是特定格式的指令&#xff0c;系统可以使用脚本解析器翻译或解析指令并执行&#xff08;它不需要编译&#xff09;。 shell脚本本质&#xff1a;shell命…

浅谈 Spring AOP框架 (2)——Spring统一功能处理

文章目录 一、AOP实战——SpringBoot统一功能处理1.1、使用拦截器实现用户登录权限的统一验证1.1.1、使用原生Spring AOP实现统一拦截的难点1.1.2、Spring 拦截器1.1.2.1、Spring拦截器 使用步骤1.1.2.2、拦截器实现原理 1.2、统一数据格式返回1.2.1、为什么要返回统一的数据格…