Linux系统编程---线程池并发服务器

news2024/11/25 11:49:37

模型原理分析:

线程池的关键优势在于它减少了每次任务执行时创建和销毁线程的开销

线程池的组成主要分为 3 个部分,这三部分配合工作就可以得到一个完整的线程池: 

1. 任务队列,存储需要处理的任务,由工作的线程来处理这些任务

        通过线程池提供的 API 函数,将一个待处理的任务(客户端提交)添加到任务队列,或者从任务队列中删除,已处理的任务会从任务队列中删除;线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程即生产者线程

2. 工作的线程(任务队列任务的消费者) ,N个

        线程池中维护了一定数量的工作线程,他们的作用是是不停的读任务队列,从里边取出任务并处理;工作的线程相当于是任务队列的消费者角色;如果任务队列为空,工作的线程将会被阻塞 (使用条件变量 / 信号量阻塞);如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作

3. 管理者线程(不处理任务队列中的任务),1个

        它的任务是周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测。当任务过多的时候,可以适当的创建一些新的工作线程。当任务过少的时候,可以适当的销毁一些工作的线程

线程池的工作原理的基本步骤: 

1. 初始化:

        在程序开始时或者当需要使用多线程时,创建一定数量的线程,这些线程通常会在一个阻塞状态下等待任务的分配。这些线程被创建并加入到池中,形成一个可用线程的集合。

2. 等待任务:

        线程池中的线程通常会等待任务队列,直到有任务提交给线程池执行。

3. 任务的分配:

        当一个外部线程(线程池的客户端)提交一个任务时,任务会被添加到任务队列中。线程池中的某个线程(通常是队列中的第一个线程)会从队列中取出任务并执行。

4. 执行任务:

        被分配任务的线程会执行具体的任务,在任务执行期间,该线程不会对其他任务可用。

5. 任务完成:

        一旦任务完成,线程会报告任务完成,并且变得再次可用于执行队列中的下一个任务,如果任务执行过程中产生了异常,异常会被捕获并根据具体的线程池实现处理。

6. 线程的循环使用:

        完成任务的线程会返回到线程池中,处于等待状态,直到再次被分配新的任务

7. 关闭线程池:

        当不再需要线程池时,可以关闭线程池。这通常涉及到发送中断信号给所有等待的线程,停止它们等待新的任务。有些线程池会等待所有正在执行的任务完成后再终止线程。一旦线程池关闭,它就不能再接收新的任务了。

线程池模块分析:

1. main();        

        创建线程池。

        向线程池中添加任务。 借助回调处理任务。

        销毁线程池。

2. pthreadpool_create();

        创建线程池结构体 指针。

        初始化线程池结构体 {  N 个成员变量 }

        创建 N 个任务线程。

        创建 1 个管理者线程。

        失败时,销毁开辟的所有空间。(释放)

3. threadpool_thread()

        进入子线程回调函数。

        接收参数 void *arg  --> pool 结构体

        加锁 -->lock --> 整个结构体锁

        判断条件变量 --> wait  -------------------170

4. adjust_thread()

        循环 10 s 执行一次。

        进入管理者线程回调函数

        接收参数 void *arg  --> pool 结构体

        加锁 -->lock --> 整个结构体锁

        获取管理线程池要用的到 变量。    task_num, live_num, busy_num

        根据既定算法,使用上述3变量,判断是否应该 创建、销毁线程池中 指定步长的线程。

5. threadpool_add ()

        总功能:

            模拟产生任务。   num[20]

            设置回调函数, 处理任务。  sleep(1) 代表处理完成。

        内部实现:
            加锁

            初始化 任务队列结构体成员。   回调函数 function, arg

            利用环形队列机制,实现添加任务。 借助队尾指针挪移 % 实现。

            唤醒阻塞在 条件变量上的线程。
            解锁

6.  从 3. 中的wait之后继续执行,处理任务。

        加锁   
        获取 任务处理回调函数,及参数

        利用环形队列机制,实现处理任务。 借助队头指针挪移 % 实现。

        唤醒阻塞在 条件变量 上的 server。

        解锁

        加锁 

        改忙线程数++

        解锁

        执行处理任务的线程

        加锁 

        改忙线程数--

        解锁

7. 创建 销毁线程

        管理者线程根据 task_num, live_num, busy_num  

        根据既定算法,使用上述3变量,判断是否应该 创建、销毁线程池中 指定步长的线程。

        如果满足 创建条件

            pthread_create();   回调 任务线程函数。        live_num++

        如果满足 销毁条件

            wait_exit_thr_num = 10;  

            signal 给 阻塞在条件变量上的线程 发送 假条件满足信号    

            跳转至  --170 wait阻塞线程会被 假信号 唤醒。判断: wait_exit_thr_num  > 0 pthread_exit();      

C代码实现

头文件声明threadpool.h

#ifndef __THREADPOOL_H_
#define __THREADPOOL_H_

typedef struct threadpool_t threadpool_t;

threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size);

int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg);

int threadpool_destroy(threadpool_t *pool);

int threadpool_all_threadnum(threadpool_t *pool);

int threadpool_busy_threadnum(threadpool_t *pool);

#endif

源文件 threadpool.c:

#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "threadpool.h"

#define DEFAULT_TIME 10                 /*10s检测一次*/
#define MIN_WAIT_TASK_NUM 10            /*如果queue_size > MIN_WAIT_TASK_NUM 添加新的线程到线程池*/ 
#define DEFAULT_THREAD_VARY 10          /*每次创建和销毁线程的个数*/
#define true 1
#define false 0

typedef struct {
    void *(*function)(void *);          /* 函数指针,回调函数 */
    void *arg;                          /* 上面函数的参数 */
} threadpool_task_t;                    /* 各子线程任务结构体 */

/* 描述线程池相关信息 */

struct threadpool_t {
    pthread_mutex_t lock;               /* 用于锁住本结构体 */    
    pthread_mutex_t thread_counter;     /* 记录忙状态线程个数de琐 -- busy_thr_num */

    pthread_cond_t queue_not_full;      /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */
    pthread_cond_t queue_not_empty;     /* 任务队列里不为空时,通知等待任务的线程 */

    pthread_t *threads;                 /* 存放线程池中每个线程的tid。数组 */
    pthread_t adjust_tid;               /* 存管理线程tid */
    threadpool_task_t *task_queue;      /* 任务队列(数组首地址) */

    int min_thr_num;                    /* 线程池最小线程数 */
    int max_thr_num;                    /* 线程池最大线程数 */
    int live_thr_num;                   /* 当前存活线程个数 */
    int busy_thr_num;                   /* 忙状态线程个数 */
    int wait_exit_thr_num;              /* 要销毁的线程个数 */

    int queue_front;                    /* task_queue队头下标 */
    int queue_rear;                     /* task_queue队尾下标 */
    int queue_size;                     /* task_queue队中实际任务数 */
    int queue_max_size;                 /* task_queue队列可容纳任务数上限 */

    int shutdown;                       /* 标志位,线程池使用状态,true或false */
};

void *threadpool_thread(void *threadpool);

void *adjust_thread(void *threadpool);

int is_thread_alive(pthread_t tid);
int threadpool_free(threadpool_t *pool);

//threadpool_create(3,100,100);  
threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size)
{
    int i;
    threadpool_t *pool = NULL;          /* 线程池 结构体 */

    do {
        if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) {  
            printf("malloc threadpool fail");
            break;                                      /*跳出do while*/
        }

        pool->min_thr_num = min_thr_num;
        pool->max_thr_num = max_thr_num;
        pool->busy_thr_num = 0;
        pool->live_thr_num = min_thr_num;               /* 活着的线程数 初值=最小线程数 */
        pool->wait_exit_thr_num = 0;
        pool->queue_size = 0;                           /* 有0个产品 */
        pool->queue_max_size = queue_max_size;          /* 最大任务队列数 */
        pool->queue_front = 0;
        pool->queue_rear = 0;
        pool->shutdown = false;                         /* 不关闭线程池 */

        /* 根据最大线程上限数, 给工作线程数组开辟空间, 并清零 */
        pool->threads = (pthread_t *)malloc(sizeof(pthread_t)*max_thr_num); 
        if (pool->threads == NULL) {
            printf("malloc threads fail");
            break;
        }
        memset(pool->threads, 0, sizeof(pthread_t)*max_thr_num);

        /* 给 任务队列 开辟空间 */
        pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t)*queue_max_size);
        if (pool->task_queue == NULL) {
            printf("malloc task_queue fail");
            break;
        }

        /* 初始化互斥琐、条件变量 */
        if (pthread_mutex_init(&(pool->lock), NULL) != 0
                || pthread_mutex_init(&(pool->thread_counter), NULL) != 0
                || pthread_cond_init(&(pool->queue_not_empty), NULL) != 0
                || pthread_cond_init(&(pool->queue_not_full), NULL) != 0)
        {
            printf("init the lock or cond fail");
            break;
        }

        /* 启动 min_thr_num 个 work thread */
        for (i = 0; i < min_thr_num; i++) {
            pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);   /*pool指向当前线程池*/
            printf("start thread 0x%x...\n", (unsigned int)pool->threads[i]);
        }
        pthread_create(&(pool->adjust_tid), NULL, adjust_thread, (void *)pool);     /* 创建管理者线程 */

        return pool;

    } while (0);

    threadpool_free(pool);      /* 前面代码调用失败时,释放poll存储空间 */

    return NULL;
}

/* 向线程池中 添加一个任务 */
//threadpool_add(thp, process, (void*)&num[i]);   /* 向线程池中添加任务 process: 小写---->大写*/

int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg)
{
    pthread_mutex_lock(&(pool->lock));

    /* ==为真,队列已经满, 调wait阻塞 */
    while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown)) {
        pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));
    }

    if (pool->shutdown) {
        pthread_cond_broadcast(&(pool->queue_not_empty));
        pthread_mutex_unlock(&(pool->lock));
        return 0;
    }

    /* 清空 工作线程 调用的回调函数 的参数arg */
    if (pool->task_queue[pool->queue_rear].arg != NULL) {
        pool->task_queue[pool->queue_rear].arg = NULL;
    }

    /*添加任务到任务队列里*/
    pool->task_queue[pool->queue_rear].function = function;
    pool->task_queue[pool->queue_rear].arg = arg;
    pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size;       /* 队尾指针移动, 模拟环形 */
    pool->queue_size++;

    /*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/
    pthread_cond_signal(&(pool->queue_not_empty));
    pthread_mutex_unlock(&(pool->lock));

    return 0;
}

/* 线程池中各个工作线程 */
void *threadpool_thread(void *threadpool)
{
    threadpool_t *pool = (threadpool_t *)threadpool;
    threadpool_task_t task;

    while (true) {
        /* Lock must be taken to wait on conditional variable */
        /*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/
        pthread_mutex_lock(&(pool->lock));

        /*queue_size == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/
        while ((pool->queue_size == 0) && (!pool->shutdown)) {  
            printf("thread 0x%x is waiting\n", (unsigned int)pthread_self());
            pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));

            /*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
            if (pool->wait_exit_thr_num > 0) {
                pool->wait_exit_thr_num--;

                /*如果线程池里线程个数大于最小值时可以结束当前线程*/
                if (pool->live_thr_num > pool->min_thr_num) {
                    printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());
                    pool->live_thr_num--;
                    pthread_mutex_unlock(&(pool->lock));

                    pthread_exit(NULL);
                }
            }
        }

        /*如果指定了true,要关闭线程池里的每个线程,自行退出处理---销毁线程池*/
        if (pool->shutdown) {
            pthread_mutex_unlock(&(pool->lock));
            printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());
            pthread_detach(pthread_self());
            pthread_exit(NULL);     /* 线程自行结束 */
        }

        /*从任务队列里获取任务, 是一个出队操作*/
        task.function = pool->task_queue[pool->queue_front].function;
        task.arg = pool->task_queue[pool->queue_front].arg;

        pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size;       /* 出队,模拟环形队列 */
        pool->queue_size--;

        /*通知可以有新的任务添加进来*/
        pthread_cond_broadcast(&(pool->queue_not_full));

        /*任务取出后,立即将 线程池琐 释放*/
        pthread_mutex_unlock(&(pool->lock));

        /*执行任务*/ 
        printf("thread 0x%x start working\n", (unsigned int)pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));                            /*忙状态线程数变量琐*/
        pool->busy_thr_num++;                                                   /*忙状态线程数+1*/
        pthread_mutex_unlock(&(pool->thread_counter));

        (*(task.function))(task.arg);                                           /*执行回调函数任务*/
        //task.function(task.arg);                                              /*执行回调函数任务*/

        /*任务结束处理*/ 
        printf("thread 0x%x end working\n", (unsigned int)pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thr_num--;                                       /*处理掉一个任务,忙状态数线程数-1*/
        pthread_mutex_unlock(&(pool->thread_counter));
    }

    pthread_exit(NULL);
}

/* 管理线程 */
void *adjust_thread(void *threadpool)
{
    int i;
    threadpool_t *pool = (threadpool_t *)threadpool;
    while (!pool->shutdown) {

        sleep(DEFAULT_TIME);                                    /*定时 对线程池管理*/

        pthread_mutex_lock(&(pool->lock));
        int queue_size = pool->queue_size;                      /* 关注 任务数 */
        int live_thr_num = pool->live_thr_num;                  /* 存活 线程数 */
        pthread_mutex_unlock(&(pool->lock));

        pthread_mutex_lock(&(pool->thread_counter));
        int busy_thr_num = pool->busy_thr_num;                  /* 忙着的线程数 */
        pthread_mutex_unlock(&(pool->thread_counter));

        /* 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时 如:30>=10 && 40<100*/
        if (queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num) {
            pthread_mutex_lock(&(pool->lock));  
            int add = 0;

            /*一次增加 DEFAULT_THREAD 个线程*/
            for (i = 0; i < pool->max_thr_num && add < DEFAULT_THREAD_VARY
                    && pool->live_thr_num < pool->max_thr_num; i++) {
                if (pool->threads[i] == 0 || !is_thread_alive(pool->threads[i])) {
                    pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);
                    add++;
                    pool->live_thr_num++;
                }
            }

            pthread_mutex_unlock(&(pool->lock));
        }

        /* 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时*/
        if ((busy_thr_num * 2) < live_thr_num  &&  live_thr_num > pool->min_thr_num) {

            /* 一次销毁DEFAULT_THREAD个线程, 隨機10個即可 */
            pthread_mutex_lock(&(pool->lock));
            pool->wait_exit_thr_num = DEFAULT_THREAD_VARY;      /* 要销毁的线程数 设置为10 */
            pthread_mutex_unlock(&(pool->lock));

            for (i = 0; i < DEFAULT_THREAD_VARY; i++) {
                /* 通知处在空闲状态的线程, 他们会自行终止*/
                pthread_cond_signal(&(pool->queue_not_empty));
            }
        }
    }

    return NULL;
}

int threadpool_destroy(threadpool_t *pool)
{
    int i;
    if (pool == NULL) {
        return -1;
    }
    pool->shutdown = true;

    /*先销毁管理线程*/
    pthread_join(pool->adjust_tid, NULL);

    for (i = 0; i < pool->live_thr_num; i++) {
        /*通知所有的空闲线程*/
        pthread_cond_broadcast(&(pool->queue_not_empty));
    }
    for (i = 0; i < pool->live_thr_num; i++) {
        pthread_join(pool->threads[i], NULL);
    }
    threadpool_free(pool);

    return 0;
}

int threadpool_free(threadpool_t *pool)
{
    if (pool == NULL) {
        return -1;
    }

    if (pool->task_queue) {
        free(pool->task_queue);
    }
    if (pool->threads) {
        free(pool->threads);
        pthread_mutex_lock(&(pool->lock));
        pthread_mutex_destroy(&(pool->lock));
        pthread_mutex_lock(&(pool->thread_counter));
        pthread_mutex_destroy(&(pool->thread_counter));
        pthread_cond_destroy(&(pool->queue_not_empty));
        pthread_cond_destroy(&(pool->queue_not_full));
    }
    free(pool);
    pool = NULL;

    return 0;
}

int threadpool_all_threadnum(threadpool_t *pool)
{
    int all_threadnum = -1;                 // 总线程数

    pthread_mutex_lock(&(pool->lock));
    all_threadnum = pool->live_thr_num;     // 存活线程数
    pthread_mutex_unlock(&(pool->lock));

    return all_threadnum;
}

int threadpool_busy_threadnum(threadpool_t *pool)
{
    int busy_threadnum = -1;                // 忙线程数

    pthread_mutex_lock(&(pool->thread_counter));
    busy_threadnum = pool->busy_thr_num;    
    pthread_mutex_unlock(&(pool->thread_counter));

    return busy_threadnum;
}

int is_thread_alive(pthread_t tid)
{
    int kill_rc = pthread_kill(tid, 0);     //发0号信号,测试线程是否存活
    if (kill_rc == ESRCH) {
        return false;
    }
    return true;
}

/*测试*/ 

#if 1

/* 线程池中的线程,模拟处理业务 */
void *process(void *arg)
{
    printf("thread 0x%x working on task %d\n ",(unsigned int)pthread_self(),(int)arg);
    sleep(1);                           //模拟 小---大写
    printf("task %d is end\n",(int)arg);

    return NULL;
}

int main(void)
{
    /*threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size);*/

    threadpool_t *thp = threadpool_create(3,100,100);   /*创建线程池,池里最小3个线程,最大100,队列最大100*/
    printf("pool inited");

    //int *num = (int *)malloc(sizeof(int)*20);
    int num[20], i;
    for (i = 0; i < 20; i++) {
        num[i] = i;
        printf("add task %d\n",i);
        
        /*int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg) */

        threadpool_add(thp, process, (void*)&num[i]);   /* 向线程池中添加任务 */
    }

    sleep(10);                                          /* 等子线程完成任务 */
    threadpool_destroy(thp);

    return 0;
}

#endif

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

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

相关文章

python代码实现kmeans对鸢尾花聚类

导入第三方库和模型 from sklearn import datasets import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import KMeans2、创建画图函数 def draw_result(train_x, labels, cents, title):n_clusters np.unique(labels).shape[0]#获取类别个数color …

esp32s3使用psram后音频播报不了的问题解决记录

idf.py menuconfig开启psram后会报错 提示需要打补丁&#xff1a; 根据提示切换到IDF_PATH目录&#xff0c;然后执行git apply %ADF_PATH%/ida_patches/idf5.0_freertos.patch打补丁。 再次编译提示如下错误&#xff1a; assert failed: spi_flash_disable_interrupts_cach…

嵌入式学习,方法、交流很重要

关注、星标公众号&#xff0c;直达精彩内容 ID&#xff1a;技术让梦想更伟大 整理&#xff1a;李肖遥 Who Am I 大家好&#xff0c;我是「逍遥的小蜜圈」星球的星主&#xff0c;如果大家关注我早一点&#xff0c;一定看了我的简单的自我介绍&#xff0c;关于我 — 聊聊自己的经…

【Python网络爬虫】python爬虫用正则表达式进行数据清洗与处理

&#x1f517; 运行环境&#xff1a;PYTHON &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

QT学习篇—qt软件安装

qt下载网址http://download.qt.io/new_archive/qt/ QT官网Qt | Tools for Each Stage of Software Development LifecycleAll the essential Qt tools for all stages of Software Development Lifecycle: planning, design, development, testing, and deployment.https:…

CSS样式特异性5层次详解

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

FANUC机器人SOCKET断开KAREL程序编写

一、添加一个.KL文件创建编辑断开指令 添加一个KL文件用来创建karel程序中socket断开指令 二、断开连接程序karel代码 PROGRAM SOC_DIS %COMMENT SOCKET断开 %INCLUDE klevccdf VAR str_input,str_val : STRING[20] status,data_type,int_val : INTEGER rel_val : REALBEGING…

全球首发!龙蜥社区助力 Intel SPR 加速器上云

编者按&#xff1a;云原生平台下芯片的竞争力日渐增强&#xff0c;加速器如何在赛道上体现竞争力。龙蜥社区开发者、阿里云高级研发工程师易兴睿介绍运用龙蜥操作系统提供的解决方案&#xff0c;依靠 Intel SPR 平台专用硬件加速器&#xff0c;实现云原生场景下 Envoy 网关加速…

微信小程序 request 配置了服务器域名后 发布体验版无法访问

问题描述 在微信小程序公众平台配置了测试服务器域名后&#xff0c;发布了体验版进行测试&#xff0c;发现网络请求不通&#xff0c;打开调试也依然无法访问。 解决步骤&#xff1a; 1.首先根据小程序文档网络模块的使用说明&#xff0c;一步步排查域名证书是否符合规范&…

Llama3 mac本地部署教程

1.下载的软件清单&#xff1a; ollama下载&#xff1a; Download Ollama on macOS nodejs下载&#xff1a; Node.js — Download Node.js 2.安装 安装Ollama 下载之后打开&#xff0c;直接点击Next以及Install安装ollama到命令行。安装完成后界面上会提示ollama run llam…

在Docker中部署Java应用:Java版本隔离的实践案例

在Docker中部署Java应用&#xff1a;Java版本隔离的实践案例 人生就是一场又一场的相遇&#xff0c;一个明媚&#xff0c;一个忧伤&#xff0c;一个华丽&#xff0c;一个冒险&#xff0c;一个倔强&#xff0c;一个柔软&#xff0c;最后那个正在成长。 背景需求 在软件开发和部…

18 python定制篇-开发平台Ubuntu

第 18 章Linux 之 Python 定制篇-Python 开发平台 Ubuntu 18.1 Ubuntu 介绍 Ubuntu&#xff08;友帮拓、优般图、乌班图&#xff09;是一个以桌面应用为主的开源 GNU/Linux 操作系统&#xff0c;Ubuntu 是基于 GNU/Linux&#xff0c; 支持 x86、amd64&#xff08;即 x64&…

PG修改端口号与error: could not connect to server: could not connect to server 问题解决

刚开始学习PG修改端口号之后数据库端口号没变。 修改端口号&#xff1a;/usr/local/pgsql/data中的postgresql.conf中 修改后并不能直接生效需要重启PG&#xff1a; /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l /usr/local/pgsql/data/logfile restart重启后新…

如何免费生成文本二维码?文字生成二维码的方法

随着信息技术的不断发展&#xff0c;文本二维码作为一种简便、高效的信息分享方式&#xff0c;受到了越来越多人的关注和应用。文本二维码是将文本信息编码成二维码的形式&#xff0c;通过扫描二维码即可快速获取文本内容&#xff0c;为信息分享和传播提供了全新的可能性。 便…

美富特 | 邀您参加2024全国水科技大会暨技术装备成果展览会

王涛 四川美源环能科技有限公司 技术总监 报告题目&#xff1a;绿色智慧水岛如何助力工业园区污水及再生水资源化利用降碳增效 拥有十余年的环保行业从业经验&#xff0c;对各类前沿物化、生化及膜技术均有丰富的研发、设计及应用经验&#xff0c;先后参与多项重点核心技术…

跨境电商亚马逊、虾皮等平台做测评要用什么IP?

IP即IP地址&#xff0c;IP地址是指互联网协议地址&#xff08;英语&#xff1a;Internet Protocol Address&#xff0c;又译为网际协议地址&#xff09;&#xff0c;是IP Address的缩写&#xff0c;IP地址是IP协议提供的一种统一的地址格式 功能&#xff1a;它为互联网上的每一…

密码学python库PBC安装使用

初始化 使用环境云服务器&#xff08;移动云可以免费使用一个月&#xff09; 选择ubuntu18.04-64位 第一次进入linux命令行之后是没有界面显示的&#xff0c;需要在命令行下载。 这里按照其他云平台操作即可&#xff1a;Ubuntu18.04 首次使用配置教程(图形界面安装) 记录好登录…

软件工程物联网方向嵌入式系统复习笔记--嵌入式系统基础

1 嵌入式系统基础 1.1 嵌入式系统基础 1.1.1 嵌入式系统概念 嵌入式系统一般定义 是指以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。 就像一般的计算机系统包括软件和硬件一样&#xff0c;…

Python: Regular expressions

# encoding: utf-8 # 版权所有 2024 ©涂聚文有限公司 # 许可信息查看&#xff1a; # 描述&#xff1a; 正则表达式用法 # Author : geovindu,Geovin Du 涂聚文. # IDE : PyCharm 2023.1 python 311 # Datetime : 2024/4/28 10:37 # User : geovindu # Prod…

德思特手持式频谱分析仪加速公共无线事业的运行

一、公共无线事业中无线电的重要性 提起无线电&#xff0c;许多人或许觉得这是非常老旧的观念。实际上&#xff0c;无线电在我们的生活中占据着非常重要的地位。当前&#xff0c;无线电早已成为受众最广泛的大众媒介。据工业和信息化部资料显示&#xff0c;全球95%的人口都可以…