【高并发服务器 02】——线程池与IO多路复用

线程池复习

  • 线程池的好处:所有的池都是为了事先把资源准备好,在后续用的时候可以更加方便的拿到这个资源——不用去申请、释放资源

  • 什么时候用线程池

    • IO&事务并发较高:人在杭州,但是数据库在北京,想要查询数据库,需要通过互联网建立TCP三次握手,频繁地创建和销毁线程意味着频繁地进行TCP三次握手,四次挥手。如果实际查询数据库的时间不到建立连接时间的一般甚至更少。那么这种代价是我们不能忍受的。而池化技术,就是这样一种,创建好了线程,完成了TCP连接,不需要频繁创建和释放资源的技术。
  • 线程的数量多少合适?

    • 小于等于CPU的核心数:我们知道线程是CPU调度的基本单位,调度的目的就是使他能够在CPU上运行,同一时间最多线程能够运行的个数取决于CPU的核心数

    • IO密集型:进程来了之后就做一件事情——申请1次IO。就像我们去银行取钱,你要做的无非就是让柜台给我取钱。我们处在上层的应用发起一次请求,交给内核下层去做,内核做的过程中,有这种IO管理上的通道技术、IO控制器,所以并非是CPU在处理拿数据。如果线程是处于阻塞等的状态,那么这个线程就暂时处于失联了的状态,这个时候线程数量过少了也不行。但是如果是异步IO,非阻塞,这个时候就跟CPU密集型是一样的。因为不会在IO上发生等待。

    • CPU密集型:比如计算一个非常复杂的式子,很多个线程协同计算这件事情,CPU有多少潜能都能被榨干。这个时候,线程的数量一定不能大于CPU核心数。

  • 线程池的实现

    • 任务队列:队列的定义、初始化、push、pop、销毁
    • 一组线程:线程处理函数
  • 死锁:两个或两个以上的进程因为推进顺序不当或资源数量受限而导致的互相等待的情况叫做死锁。

  • 惊群效应pthread_cond_wait这条语句之前必须上锁。执行到它时会首先把锁给解开,然后进去睡眠等待,直到有一个信号来唤醒它,唤醒了之后又去抢锁,这个时候,有一堆人抢锁,第一个抢到锁的人,率先把资源给吃了,接着把锁释放,剩下的线程继续抢锁,抢到锁之后发现队列还是空的,于是又把锁丢掉,进入了睡眠直到下一个信号把它唤醒。

编程技巧

  • #ifdef宏定义

    • #ifdef是一个预处理指令,用于在编译时根据条件判断是否编译特定的代码块。它的作用是根据条件判断编译不同的代码,可以用来实现条件编译。当条件为真时,编译#ifdef和#endif之间的代码;当条件为假时,忽略#ifdef和#endif之间的代码。这样可以根据不同的编译条件,在不同的环境中编译不同的代码,实现对不同平台、不同需求的适配。

    • #ifndef 的作用就是确保只定义一次

#ifndef _HEAD_H
#define _HEAD_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "thread_pool.h"
#include "color.h"
#include "common.h" // 自己的库写在后面,每新写一个库就往后面放,确保引用顺序正确
#endif
  • 编译调试命令:gcc test.c -I common/ -D _D

    • -I common/:这个选项告诉编译器在哪里查找头文件。编译器在编译程序时,除了查找标准的头文件位置外,还会在这里指定的目录中查找。在这个例子中,编译器会在名为common/的目录下查找头文件。这个目录通常包含了一些公共的或者共享的头文件。

    • -D _D:这个选项用于定义宏。-D选项后面跟着的是宏的名称(在这个例子中是_D),它相当于在源代码的最开始添加了一行#define _D。这样,你就可以在代码中通过预处理指令#ifdef、#ifndef或#if defined等来检查这个宏是否被定义,从而使得代码可以根据宏的定义与否来选择性地编译。这在条件编译中非常有用。

#ifdef _D
#define DBG(fmt, args...) printf(fmt, ##args)
#else
#define DBG(fmt, args...)
#endif
  • 颜色调试
    我们希望在debug输出的时候能够以彩色的形式输出更多更丰富的类别信息。
#ifndef _COLOR_H
#define _COLOR_H#define NONE      "\e[0m"     // 清除颜色,即之后的打印为正常输出,之前的不受影响
#define BLACK     "\e[0;30m"  // 深黑
#define L_BLACK   "\e[1;30m"  // 亮黑,偏灰褐
#define RED       "\e[0;31m"  // 深红
#define L_RED     "\e[1;31m"  // 鲜红
#define GREEN     "\e[0;32m"  // 深绿
#define L_GREEN   "\e[1;32m"  // 鲜绿色
#define BROWN     "\e[0;33m"  // 深黄
#define YELLOW    "\e[1;33m"  // 鲜黄
#define BLUE      "\e[0;34m"  // 深蓝
#define L_BLUE    "\e[1;34m"  // 亮蓝
#define PINK      "\e[0;35m"  // 深粉
#define L_PINK    "\e[1;35m"  // 亮粉
#define CYAN      "\e[0;36m"  // 暗青色
#define L_CYAN    "\e[1;36m"  // 亮青色
#define GRAY      "\e[0;37m"  // 灰色
#define WHITE     "\e[1;37m"  // 白色,字体比正常大,比bold小
#define BOLD      "\e[1m"     // 白色,粗体
#define UNDERLINE "\e[4m"     // 下划线,白色,正常大小
#define BLINK     "\e[5m"     // 闪烁,白色,正常大小
#define REVERSE   "\e[7m"     // 反转,即字体背景为白色,字体为黑色
#define HIDE      "\e[8m"     // 隐藏
#define CLEAR     "\e[2J"     // 清除
#define CLRLINE   "\e[K"      // 清除行#endif

代码实现:

/*************************************************************************> File Name: common.h> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 09:01:03 AM CST************************************************************************/#ifndef _COMMON_H // 如果没有定义_COMMON_H,那么执行下面的代码
#define _COMMON_H // 定义_COMMON_H,防止头文件被重复包含// 定义一个全局数组,用于存储配置文件的答案或其他用途
char conf_ans[512];// 声明socket_create函数,该函数用于创建一个监听socket
// 参数port是要绑定的端口号
int socket_create(int port);// 声明socket_connect函数,该函数用于创建一个连接到指定IP和端口的socket
// 参数ip是要连接的目标IP地址,port是目标端口号
int socket_connect(const char *ip, int port);// 声明make_block函数,该函数用于将指定的文件描述符设置为阻塞模式
// 参数fd是要设置的文件描述符
int make_block(int fd);// 声明make_nonblock函数,该函数用于将指定的文件描述符设置为非阻塞模式
// 参数fd是要设置的文件描述符
int make_nonblock(int fd);#endif // 结束条件编译,与#ifndef对应
/*************************************************************************> File Name: head.h> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 08:56:01 AM CST************************************************************************/#ifndef _HEAD_H
#define _HEAD_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include "thread_pool.h"
#include "color.h"
#include "common.h"
#ifdef _D
#define DBG(fmt, args...) printf(fmt, ##args)
#else
#define DBG(fmt, args...)
#endif
#endif
/*************************************************************************> File Name: common.c> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 08:56:39 AM CST************************************************************************/#include "head.h" // 引入相关的头文件// 创建一个监听socket的函数
int socket_create(int port) {int sockfd; // 用于存储socket文件描述符// 创建socket,AF_INET表示使用IPv4协议,SOCK_STREAM表示使用TCP协议// 成功时返回socket描述符,失败时返回-1if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {return -1; // 创建socket失败,返回-1}struct sockaddr_in addr; // 定义IPV4的sock地址结构addr.sin_family = AF_INET; // 指定地址家族为IPv4addr.sin_port = htons(port); // 指定端口号,并使用htons函数将主机字节顺序转换为网络字节顺序addr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 指定接收所有IP地址的连接int reuse = 1; // 定义重用标志// 设置socket选项,允许重用本地地址和端口,这对于防止“地址已在使用”错误很有帮助setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int));// 将socket绑定到指定的IP地址和端口if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {return -1; // 绑定失败,返回-1}// 开始监听,第二个参数为backlog,表示内核监听队列的最大长度if (listen(sockfd, 8) < 0) {return -1; // 监听失败,返回-1}return sockfd; // 成功创建并初始化socket,返回socket文件描述符
}
/*************************************************************************> File Name: thread_pool.c> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 09:23:56 AM CST************************************************************************/#include "head.h" // 包含自定义头文件,可能包含所需的库文件和宏定义// 初始化任务队列
void task_queue_init(struct task_queue* taskQueue, int size) {taskQueue->size = size; // 设置队列大小taskQueue->total = taskQueue->head = taskQueue->tail = 0; // 初始化队列的状态taskQueue->data = calloc(sizeof(void *), size); // 分配存储任务的内存空间pthread_mutex_init(&taskQueue->mutex, NULL); // 初始化互斥锁pthread_cond_init(&taskQueue->cond, NULL); // 初始化条件变量return ;
}// 向任务队列中添加任务
void task_queue_push(struct task_queue *taskQueue, void *data) {pthread_mutex_lock(&taskQueue->mutex); // 加锁保护队列状态if (taskQueue->total == taskQueue->size) { // 队列已满,无法添加DBG(YELLOW"<push> taskQueue is full.\n"NONE);pthread_mutex_unlock(&taskQueue->mutex); // 解锁后返回return ;}taskQueue->data[taskQueue->tail] = data; // 将任务添加到队尾DBG(PINK"<push> push to %dth task.\n"NONE, taskQueue->tail);taskQueue->total++; // 更新队列中的任务总数if (++taskQueue->tail == taskQueue->size) { // 如果队尾指针到达队列末尾,重置为0DBG(PINK"<push> tail begins with 0.\n"NONE);taskQueue->tail = 0;}pthread_cond_signal(&taskQueue->cond); // 通知等待的线程有新任务添加pthread_mutex_unlock(&taskQueue->mutex); // 解锁
}// 从任务队列中取出任务
void* task_queue_pop(struct task_queue *taskQueue) {pthread_mutex_lock(&taskQueue->mutex); // 加锁while (taskQueue->total == 0) { // 如果队列为空,则等待pthread_cond_wait(&taskQueue->cond, &taskQueue->mutex);}void *data = taskQueue->data[taskQueue->head]; // 从队头取出任务DBG(BLUE"<pop> pop data from %dth task.\n"NONE, taskQueue->head);taskQueue->total--; // 更新队列中的任务总数if (++taskQueue->head == taskQueue->size) { // 如果队头指针到达队列末尾,重置为0DBG(PINK"<pop> head begins with 0.\n"NONE);taskQueue->head = 0;}pthread_mutex_unlock(&taskQueue->mutex); // 解锁return data; // 返回取出的任务数据
}// 线程执行函数
void* thread_run(void *arg) {pthread_detach(pthread_self()); // 设置线程为分离状态,结束时自动释放资源struct task_queue *taskQueue = (struct task_queue *)arg; // 获取任务队列while (1) {void* data = task_queue_pop(taskQueue); // 从任务队列中取出任务DBG(GREEN"<thread> got a task! pop the data %s"NONE, (char *)data);// 在这里处理任务,示例代码中仅打印消息}
}
/*************************************************************************> File Name: thread_pool.h> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 09:24:02 AM CST************************************************************************/#ifndef _THREAD_POOL_H // 预处理指令,防止头文件被多次重复包含
#define _THREAD_POOL_H// 定义任务队列的结构体
struct task_queue {int head, tail, size, total; // 分别表示队列的头部、尾部、大小和当前任务总数void **data; // 指向任务数据的指针数组pthread_mutex_t mutex; // 互斥锁,用于同步对任务队列的访问pthread_cond_t cond; // 条件变量,用于线程间的同步
};// 声明任务队列初始化函数
void task_queue_init(struct task_queue *taskQueue, int size);// 声明任务队列添加任务函数
void task_queue_push(struct task_queue *taskQueue, void *data);// 声明从任务队列中取出任务的函数
void *task_queue_pop(struct task_queue *taskQueue);// 声明线程运行函数
void *thread_run(void *arg);#endif // 结束预处理指令
/*************************************************************************> File Name: 2.select.c> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 09:41:51 PM CST************************************************************************/#include "head.h"
#define MAX 100
#define INS 5
int main (int argc, char **argv) {if (argc < 2) {fprintf(stderr, "Usage: %s port", argv[0]);exit(1);}int server_listen, port, sockfd;pthread_t tid[INS];int clients[MAX] = {0};char buff[MAX][1024];struct task_queue *taskQueue = (struct task_queue *)malloc(sizeof(struct task_queue)); task_queue_init(taskQueue, MAX);for (int i = 0; i < INS; i++) {pthread_create(&tid[i], NULL, thread_run, (void *)taskQueue);}port = atoi(argv[1]);if ((server_listen = socket_create(port)) < 0) {perror("server_port");exit(1);}DBG(GREEN"connect to client , server_listen fd = %d.\n"NONE, server_listen);fd_set rfds;int max_fd;max_fd = server_listen;clients[server_listen] = server_listen;while (1) {FD_ZERO(&rfds);FD_SET(server_listen, &rfds);for (int i = 3; i < max_fd + 1; i++) { // 每次都要重新注册if (clients[i] == -1) continue;FD_SET(clients[i], &rfds);DBG(GREEN"SET %d in rfds.\n", clients[i]);}int ret = select(max_fd + 1, &rfds, NULL, NULL, NULL); if (ret < 0) {perror("select");exit(1);}if (FD_ISSET(server_listen, &rfds)) {if ((sockfd = accept(server_listen, NULL, NULL)) < 0) {perror("accpet");exit(1);}if (sockfd > max_fd) max_fd = sockfd;ret--;clients[sockfd] = sockfd; // 文件描述符每次选最小的数字DBG(CYAN"clients[%d] = %d.\n"NONE, sockfd, clients[sockfd]);}for (int i = 0; i < max_fd + 1; i++) {if (clients[i] == server_listen) continue;if (FD_ISSET(clients[i], &rfds)) {int rsize = recv(clients[i], buff[i], 1024, 0);DBG(CYAN"clients[%d], rsize = %d.\n"NONE, clients[i], rsize);if (rsize <= 0) {close(clients[i]);clients[i] = -1;} else {task_queue_push(taskQueue, buff[i]);}if (--ret == 0) break; // 提高效率,避免全部遍历}}		}
}
/*************************************************************************> File Name: thread_pool test.c> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 09:09:44 AM CST************************************************************************/#include"head.h" // 包含头文件,这个头文件可能包含了程序需要的其他库文件、宏定义、函数声明等
#define INS 5 // 定义常量INS,表示创建的线程数为5
#define MAX 100 // 定义常量MAX,表示缓冲区可以存储的最大行数为100int main () {FILE *fp; // 定义文件指针fp,用于打开和读取文件pthread_t tid[INS]; // 定义线程ID数组,数组大小为INS,用于存储线程IDchar buff[MAX][1024]; // 定义一个二维字符数组buff,用于存储从文件中读取的每行数据struct task_queue* taskQueue = (struct task_queue *)malloc(sizeof(struct task_queue)); // 动态分配任务队列结构体的内存task_queue_init(taskQueue, MAX); // 初始化任务队列for (int i = 0; i < INS; i++) { // 循环创建INS个线程pthread_create(&tid[i], NULL, thread_run, (void *)taskQueue); // 创建线程,线程执行的函数为thread_run,参数为任务队列的指针}int sub = 0; // 定义变量sub,用于记录当前读取到buff数组的哪一行while (1) { // 无限循环,不断从文件读取数据并处理int sub = 0; // 每次循环开始时重置sub为0if ((fp = fopen("./a.txt", "r")) == NULL) { // 尝试打开文件a.txt,如果失败,则打印错误信息并退出程序perror("fopen");exit(1);}while (fgets(buff[sub], 1024, fp) != NULL) { // 从文件中读取一行,存储到buff[sub]中,直到文件结束task_queue_push(taskQueue, buff[sub]); // 将读取到的行数据推送到任务队列中// sleep(1); // 可以根据需要取消注释,使程序在每次推送后暂停1秒if (++sub == MAX) sub = 0; // 如果sub达到MAX,则重置为0,实现循环使用buff数组}fclose(fp); // 关闭文件}return 0; // 程序正常退出
}

其中,a.txt文件的生成:

shell命令:while [[ 1 ]] do
cat test.c > a.txt
donectrl + c停止
/*************************************************************************> File Name: 2.select.c> Author: jby> Mail: > Created Time: Thu 21 Mar 2024 09:41:51 PM CST************************************************************************/#include "head.h" // 包含自定义的头文件,可能包括必要的库文件和宏定义
#define MAX 100 // 定义最大客户端数量
#define INS 5 // 定义线程池中线程的数量
int main (int argc, char **argv) {if (argc < 2) { // 检查命令行参数数量,确保提供了端口号fprintf(stderr, "Usage: %s port", argv[0]); // 如果没有提供端口号,打印使用方法exit(1); // 退出程序}int server_listen, port, sockfd; // 分别用于存储监听套接字、端口号和客户端套接字pthread_t tid[INS]; // 存储线程ID的数组int clients[MAX] = {0}; // 存储客户端套接字的数组,初始化为0char buff[MAX][1024]; // 存储接收数据的缓冲区struct task_queue *taskQueue = (struct task_queue *)malloc(sizeof(struct task_queue)); task_queue_init(taskQueue, MAX); // 初始化任务队列for (int i = 0; i < INS; i++) {pthread_create(&tid[i], NULL, thread_run, (void *)taskQueue); // 创建处理任务的线程}port = atoi(argv[1]); // 从命令行参数获取端口号if ((server_listen = socket_create(port)) < 0) { // 创建监听套接字perror("server_port"); // 如果创建失败,打印错误信息exit(1); // 退出程序}DBG(GREEN"connect to client , server_listen fd = %d.\n"NONE, server_listen); // 打印监听套接字的文件描述符fd_set rfds; // 定义文件描述符集合int max_fd; // 存储文件描述符的最大值max_fd = server_listen; // 初始化最大文件描述符为监听套接字clients[server_listen] = server_listen; // 将监听套接字加入客户端数组while (1) {FD_ZERO(&rfds); // 清空文件描述符集合FD_SET(server_listen, &rfds); // 将监听套接字加入集合for (int i = 3; i < max_fd + 1; i++) { // 遍历所有可能的文件描述符if (clients[i] == -1) continue; // 如果客户端已关闭,跳过FD_SET(clients[i], &rfds); // 将活跃的客户端套接字加入集合DBG(GREEN"SET %d in rfds.\n", clients[i]);}int ret = select(max_fd + 1, &rfds, NULL, NULL, NULL); // 调用select等待活动的文件描述符if (ret < 0) {perror("select"); // 如果select调用失败,打印错误信息exit(1); // 退出程序}if (FD_ISSET(server_listen, &rfds)) { // 检查监听套接字是否有新的连接请求if ((sockfd = accept(server_listen, NULL, NULL)) < 0) {perror("accpet"); // 如果接受连接失败,打印错误信息exit(1); // 退出程序}if (sockfd > max_fd) max_fd = sockfd; // 更新最大文件描述符ret--; // 减少待处理的文件描述符数量clients[sockfd] = sockfd; // 将新客户端的套接字加入数组DBG(CYAN"clients[%d] = %d.\n"NONE, sockfd, clients[sockfd]);}for (int i = 0; i < max_fd + 1; i++) { // 遍历所有文件描述符处理数据if (clients[i] == server_listen) continue; // 跳过监听套接字if (FD_ISSET(clients[i], &rfds)) { // 检查该文件描述符是否有数据可读int rsize = recv(clients[i], buff[i], 1024, 0); // 接收数据DBG(CYAN"clients[%d], rsize = %d.\n"NONE, clients[i], rsize);if (rsize <= 0) { // 如果接收到的数据大小小于等于0,表示客户端关闭连接或出错close(clients[i]); // 关闭套接字clients[i] = -1; // 标记客户端已关闭} else {task_queue_push(taskQueue, buff[i]); // 将接收到的数据加入任务队列}if (--ret == 0) break; // 如果已处理完所有活动的文件描述符,退出循环}}		}
}

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

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

相关文章

企业网络基础设施物理安全面临全新挑战

企业网络基础设施的物理安全是确保业务连续性和数据完整性的关键组成部分。随着技术的发展和环境的变化&#xff0c;这些基础设施面临着新的挑战。以下是一些主要的挑战和的解决方案 一、机房、仓库、档案馆物理安全事件频发的挑战&#xff1a; 1.电力安全事件&#xff1a;市…

AI智能分析网关V4在养老院视频智能监控场景中的应用

随着科技的快速发展&#xff0c;智能监控技术已经广泛应用于各个领域&#xff0c;尤其在养老院这一特定场景中&#xff0c;智能监控方案更是发挥着不可或缺的作用。尤其是伴随着社会老龄化趋势的加剧&#xff0c;养老院的安全管理问题也日益凸显。为了确保老人的生活安全&#…

没有磁盘整列下的多机分布式存储:使用rysnc+多服务器文件/文件夹内容同步

目录 0.为什么要定时同步 1.程序安装 2.文件夹设置rsync使用 3.使用cron进行定时任务 0.为什么要定时同步 作为科研党&#xff0c;实验室有多个服务器&#xff0c;但是都是分批买的没有上磁盘整列&#xff0c;所以一个服务器上跑的东西并不能同步&#xff0c;有时候挂任务要…

第十二届蓝桥杯省赛CC++ 研究生组-异或数列

solution1&#xff08;通过0%&#xff09; 没看懂怎么是必胜或必败局面(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ ) #include<iostream> typedef long long ll; const int maxn 200010; ll x[maxn]; int main(){int t, n;ll a, b, turn;scanf("%d", &t);while(t--){…

防火墙(讲解)

目录 1.防火墙是什么? 2.防火墙设备 3.防火墙功能 1)出色的控制能力&#xff0c;过滤掉不安全服务 2)过滤非法用户和访问特殊站点 3)它能够对网络存取和访问进行监控审计 4.防火墙的局限 (1)防火墙有可能是可以绕过的 (2)防火墙不能防止内部出卖性攻击或者内部误操作…

vue的优缺点有那些 组件常用的有那些?

优点&#xff1a; 组件化开发&#xff0c;提升效率&#xff0c;方便复用&#xff0c;便于协同开发单页面路由易于结合其他的第三方库丰富的api方法轻量高效,虚拟DOMMVVM&#xff0c;数据驱动视图轻量级的框架 缺点&#xff1a; 缺少高阶教程和文档生态环境不如angular和re…

javascript 获取本机ip chrome 谷歌浏览器 extension 谷歌扩展

代码一&#xff1a; if (typeof window ! undefined) {var RTCPeerConnection window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;if (RTCPeerConnection)(() > {var rtc new RTCPeerConnection()rtc.createDataChannel(); //创…

Vscode初建Vue时几个需要注意的问题

首先放图 注意点1.打开文件夹时&#xff0c;可以是VUE2 或者其他&#xff0c;但不能是VUE&#xff0c;会报错 注意点2.终端输入命令“npm init -y" npm init -y -y 的含义&#xff1a;yes的意思&#xff0c;在init的时候省去了敲回车的步骤&#xff0c;生成的默认的packag…

AI智能分析网关V4在非煤矿山安全生产视频智能监管场景中的应用

近年来&#xff0c;全国非煤矿山&#xff08;&#xff08;含金属非金属矿山、尾矿库&#xff0c;以及矿泉水等其他矿山&#xff09;安全生产工作取得明显成效&#xff0c;但安全基础仍然薄弱&#xff0c;事故总量仍然较大&#xff0c;重特大事故尚未得到根本遏制&#xff0c;安…

Clickhouse异常:Exception: No operation equals between Decimal(X, X) and Float64

在使用clickhouse中的Decimal类型存储数字时&#xff0c;使用Decimal类型字段作为查询条件时&#xff0c;比如&#xff1a; SELECT COUNT(*) AS total FROM table WHERE ( my_number10.2) 会报错如下&#xff1a;Exception: No operation equals between Decimal(X, X) and F…

linux之sed编辑器指令练习

目录 一、sed编辑器 二、sed使用案例 1.1 s命令&#xff08;substitute替换&#xff09; 一、sed编辑器 sed编辑器比交互式编辑器快的多&#xff0c;可以简化数据处理任务,sed编辑器并不会修改文件&#xff0c;只会将修改后的数据&#xff0c;输出。 二、sed使用案例 首先…

Carla 自动驾驶挑战赛 搭建环境

1. 系统设置 1.1 下载CARLA排行榜包 下载打包的CARLA 排行榜版本。 将包解压到一个文件夹中&#xff0c;例如 CARLA。 在以下命令中&#xff0c;更改${CARLA_ROOT}变量以对应于您的 CARLA 根文件夹。 为了使用 CARLA Python API&#xff0c;您需要在您喜欢的环境中安装一些…

Python:类和对象

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 面向对象编程 VS 面向过程编程 在使用计算机语言进行代码编写时&#xff0c;常见的两种思路是面向对象编程和面向过程编程&#xff1a; &#xf…

SQLiteC/C++接口详细介绍sqlite3_stmt类(十一)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十二&#xff09; 43、sqlite3_reset sqlite3_reset 函数用于重置已经编…

uniapp安装axios

先npm安装 npm i axios然后在项目里面建一个utils文件&#xff0c;再建一个index.js 以下是index.js代码&#xff1a; import axios from axios; const service axios.create({baseURL: //xxxx.xxxxx.com///你的请求接口域名, timeout: 6000, // request timeoutcrossDomai…

【C++】1599. 米老鼠偷糖果

问题&#xff1a;1599. 米老鼠偷糖果 类型&#xff1a;基本运算、整数运算 题目描述&#xff1a; 米老鼠发现了厨房放了 n 颗糖果&#xff0c;它一次可以背走 a 颗&#xff0c;请问米老鼠背了 x 次之后还剩多少颗&#xff1f;&#xff08;假设 x 次之后一定有糖果剩下&#x…

Redis中的缓存击穿

缓存击穿 缓存击穿问题也叫热点key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了&#xff0c;无数的请求访问会在瞬间给数据库带来巨大压力。 &#x1f914;现象分析&#xff1a; 当线程1查询缓存时&#xff0c;未命中&#xff0c;于是从数据…

transformer的自注意力模型如何实现自注意力

Transformer | 鲁老师 所有参考来自以上的文章&#xff0c;简单来说&#xff0c;要实现自注意力&#xff0c;就得去完成 自己点乘自己的操作&#xff0c;然后得到的结果就能够通过调整矩阵参数完成训练了。 简单来说&#xff0c;构造了三个矩阵 &#xff0c;这三个矩阵就是为…

界面控件DevExpress ASP.NET Ribbon组件 - 完美复刻Office 365体验!

无论用户是喜欢传统工具栏菜单外观、样式&#xff0c;还是想在下一个项目中复制Office 365 web UI&#xff0c;DevExpress ASP.NET都提供了所需要的工具&#xff0c;帮助用户打造更好的应用程序界面。 P.S&#xff1a;DevExpress ASP.NET Web Forms Controls拥有针对Web表单&a…

基于python+vue的O2O生鲜食品订购flask-django-nodejs-php

近年来互联网络的迅猛发展和电子终端设备的普及&#xff0c;赋予了各行业充足的发展空间。微信小程序的O2O生鲜食品订购相比于传统信息技术&#xff0c;时效性是它最大的特色&#xff0c;已经在电子娱乐、经济等中发挥着举足轻重的作用。短时间内迅速扩大了线上管理系统的规模。…