当前位置: 首页 > news >正文

【Linux系统编程】06:共享内存

共享内存


OVERVIEW

  • 共享内存
      • 一、文件上锁flock
      • 二、共享内存
        • 1.关联共享内存ftok
        • 2.获取共享内存shmget
        • 3.绑定共享内存shmat
        • 4.绑定分离shmdt
        • 5.控制共享内存shmctl
      • 三、亲缘进程间通信
        • 1.共享内存写入与读取
        • 2.共享内存解绑与删除
        • 3.共享内存综合
      • 四、非亲缘进程间通信
        • 1.通过sleep同步
        • 2.通过条件变量同步

一、文件上锁flock

可以利用flock系统调用实现,当有一个进程在文件中进程写入操作时,其他进程无法在该文件中进行写操作(只能进行读操作)。

  • 多进程实现前n项数求和(利用flock实现)
#include "head.h"struct data {int now;//中间结果int sum;//求和结果
};void getnum(struct data *d) {int fd;if ((fd = open(".data", O_RDONLY)) < 0) {perror("setopen");exit(1);}read(fd, (void *)d, sizeof(struct data));close(fd);
}void setnum(struct data *d) {int fd;if ((fd = open(".data", O_RDWR | O_CREAT, 0600)) < 0) {//只有文件的所属用户(当前的进程)有访问以及写的权限perror("getopen");exit(1);}write(fd, (void *)d, sizeof(struct data));close(fd);
}void doSum(struct data *d, int max, int i) {int fd_lock;//将lock标志给到某个文件上(.lock) 通过该文件判断进程是否有资格打开另一个被保护的文件(.data)if ((fd_lock = open(".lock", O_RDONLY)) < 0) {perror("lockopen");exit(1);}while (1) {//计算的过程需要上锁flock(fd_lock, LOCK_EX);//加锁(加锁后其他进程后面计算的语句将无法执行)getnum(d);//从.data取出上个结果if (d->now >= max) break;//判断 计算结果d->now++;d->sum += d->now;setnum(d);//将计算的结果放回.dataprintf("<i am the %dth child> now = %d, sum = %d\n", i, d->now, d->sum);flock(fd_lock, LOCK_UN);//为解锁}close(fd_lock);
}int main(int argc, char *argv[]) {//多进程实现求前n项和 同一时刻只有一个进程持有该文件//计算从0到n的和 m个进程//a.out -i -n nint opt;int ins = 1, max = 100;struct data d;d.now = 0;d.sum = 0;setnum(&d);while ((opt = getopt(argc, argv, "i:n:")) != -1) {switch (opt) {case 'i':ins = atoi(optarg);break;case 'n':max = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -i num1 -n num2", argv[0]);exit(1);}}int i;pid_t pid;for (i = 0; i < ins; ++i) {//创建ins个进程对文件进行doSum操作if ((pid = fork()) < 0) {perror("fork()");exit(1);}if (pid == 0) break;}if (pid == 0) {doSum(&d, max, i);} else {for (int k = 0; k < ins; ++k) wait(NULL);}return 0;
}

image-20230224210120883

二、共享内存

允许两个或多个进程共享一个给定的存储区,由于无需复制数据,这是最快的IPC进程间通信。

1.关联共享内存ftok

image-20230224215755103

#include "head.h"int main() {//ftok将projectId与文件名字转换为键值对key_t key;if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}printf("key = 0x%x\n", key);printf("123 = 0x%x\n", 123);return 0;
}

image-20230224215900200

2.获取共享内存shmget

image-20230224212406973

#include "head.h"int main() {//1.申请一块共享内存 将projectId与文件名字转换为 共享内存键值key_t key;if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}printf("key = 0x%x\n", key);//2.根据共享内存对应的key值 和内存大小size 得到共享内存的标识符int shmid;if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) < 0) {perror("shmget");exit(1);}printf("shmid = %d\n", shmid);return 0;
}

image-20230224221845280

3.绑定共享内存shmat

image-20230225230606913

#include "head.h"int main() {//1.申请一块共享内存 将projectId与文件名字转换为 共享内存键值key_t key;if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}printf("key = 0x%x\n", key);//2.根据共享内存对应的key值 和内存大小size 得到共享内存的标识符int shmid;if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) < 0) {perror("shmget");exit(1);}printf("shmid = %d\n", shmid);//3.将进程的动态内存空间和 共享内存空间关联void *shmemory = NULL;if ((shmemory = shmat(shmid, NULL, 0)) == (void*)-1) {perror("shmat");exit(1);}sleep(10);return 0;
}

image-20230225232517349

4.绑定分离shmdt

image-20230225232744715

#include "head.h"int main() {//1.申请一块共享内存 将projectId与文件名字转换为 共享内存键值key_t key;if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}printf("key = 0x%x\n", key);//2.根据共享内存对应的key值 和内存大小size 得到共享内存的标识符int shmid;if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) < 0) {perror("shmget");exit(1);}printf("shmid = %d\n", shmid);//3.将进程的动态内存空间和 共享内存空间关联void *shmemory = NULL;if ((shmemory = shmat(shmid, NULL, 0)) == (void*)-1) {perror("shmat");exit(1);}//4.将进程的动态内存空间和 共享内存空间关联解除int flag;if ((flag = shmdt(shmemory)) < 0) {perror("shmdt");exit(1);}return 0;
}

5.控制共享内存shmctl

image-20230226002223768

#include "head.h"int main() {//1.申请一块共享内存 将projectId与文件名字转换为 共享内存键值key_t key;if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}printf("key = 0x%x\n", key);//2.根据共享内存对应的key值 和内存大小size 得到共享内存的标识符int shmid;if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) < 0) {perror("shmget");exit(1);}printf("shmid = %d\n", shmid);//3.将进程的动态内存空间和 共享内存空间关联void *shmemory = NULL;if ((shmemory = shmat(shmid, NULL, 0)) == (void*)-1) {perror("shmat");exit(1);}//4.将进程的动态内存空间和 共享内存空间关联解除int flag;if ((flag = shmdt(shmemory)) < 0) {perror("shmdt");exit(1);}//5.shmctl删除共享内存空间if ((flag = shmctl(shmid, IPC_RMID, NULL)) < 0) {perror("shmctl");exit(1);}return 0;
}

三、亲缘进程间通信

1.共享内存写入与读取

#include "head.h"//共享内存综合运用
int main() {key_t key;int shmid;pid_t pid;char *shmemory = NULL;//1.开辟一块共享内存空间//(1)申请一块共享内存 将projectId与文件名字转换为 共享内存键值if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}//(2)根据共享内存对应的key值 和内存大小size 得到共享内存的标识符if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) < 0) {perror("shmget");exit(1);}//(3)将进程的动态内存空间和 共享内存空间关联if ((shmemory = shmat(shmid, NULL, 0)) == (void *)-1) {perror("shmat");exit(1);}//2.创建子进程 进行运算操作if ((pid = fork()) < 0) {perror("fork");exit(1);}if (pid) {//父进程写入while(1) {printf("i am the father : \n");scanf("%[^\n]s", shmemory);//向共享内存中写入getchar();//吞掉回车否则不停循环sleep(2);}} else {//子进程读出while (1) {sleep(1);if (strlen(shmemory)) printf("i am the child : %s\n", shmemory);//如果共享内存空间中有数据才进行输出memset(shmemory, 0, 4096);//临时清空共享存储空间}}return 0;
}

image-20230228214143422

2.共享内存解绑与删除

#include "head.h"//共享内存综合运用
int main() {key_t key;int shmid;pid_t pid;char *shmemory = NULL;//1.开辟一块共享内存空间//(1)申请一块共享内存 将projectId与文件名字转换为 共享内存键值if ((key = ftok("1.ftok.c", 123)) < 0) {perror("ftok");exit(1);}//(2)根据共享内存对应的key值 和内存大小size 得到共享内存的标识符if ((shmid = shmget(key, 4096, IPC_CREAT | 0666)) < 0) {perror("shmget");exit(1);}//(3)将进程的动态内存空间和 共享内存空间关联if ((shmemory = shmat(shmid, NULL, 0)) == (void *)-1) {perror("shmat");exit(1);}//2.创建子进程 利用shmdt实现 一次读取一次读入操作if ((pid = fork()) < 0) {perror("fork");exit(1);}if (pid) {printf("i am the father : \n");scanf("%[^\n]s", shmemory);getchar();} else {sleep(5);if (strlen(shmemory)) printf("i am the child : %s\n", shmemory);memset(shmemory, 0, 4096);}//3.删除开辟的共享内存空间//(1)将进程的动态内存空间和 共享内存空间关联解除int flag;if ((flag = shmdt(shmemory)) < 0) {perror("shmdt");exit(1);}//(2)shmctl删除共享内存空间(父进程执行)if (pid) {wait(NULL);if ((flag = shmctl(shmid, IPC_RMID, NULL)) < 0) {perror("shmctl");exit(1);}}sleep(5);return 0;
}

image-20230228215903113

3.共享内存综合

  • 利用共享内存实现多进程前n项数求和(利用共享内存实现)
#include "head.h"struct data {int now;//中间结果int sum;//求和结果
};void doSum(struct data *d, int max, int i) {while (1) {//计算的过程需要上锁if (d->now >= max) break;//判断 计算结果d->now++;d->sum += d->now;printf("<i am the %dth child> now = %d, sum = %d\n", i, d->now, d->sum);}
}int main(int argc, char *argv[]) {//1.命令行解析 a.out -i -n nint opt;int ins = 1, max = 100;// struct data d;// d.now = 0;// d.sum = 0;// setnum(&d);while ((opt = getopt(argc, argv, "i:n:")) != -1) {switch (opt) {case 'i':ins = atoi(optarg);break;case 'n':max = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -i num1 -n num2", argv[0]);exit(1);}}//2.共享内存的创建于绑定key_t key;int shmid;struct data *shmemory = NULL;//2.1申请一块共享内存 将projectId与文件名字转换为 共享内存键值if ((key = ftok("5.shm_sum.c", 123)) == -1) {perror("ftok");exit(1);}//2.2根据共享内存对应的key值 和内存大小size 得到共享内存的标识符if ((shmid = shmget(key, sizeof(struct data), IPC_CREAT | 0600)) < 0) {perror("shmget");exit(1);}//2.3将进程的动态内存空间和 共享内存空间关联if ((shmemory = (struct data *)shmat(shmid, NULL, 0)) == (struct data *)-1) {perror("shmat");exit(1);}shmemory->now = 0;shmemory->sum = 0;//3.创建ins个子进程对文件进行doSum操作int i;pid_t pid;for (i = 0; i < ins; ++i) {if ((pid = fork()) < 0) {perror("fork()");exit(1);}if (pid == 0) break;}if (pid == 0) {doSum(shmemory, max, i);} else {for (int k = 0; k < ins; ++k) wait(NULL);printf("%d\n", shmemory->sum);}return 0;
}

image-20230228223341746

image-20230228223606341

成功输出结果,前100项求和的结果为5050,但是当使用程序求前10000项和时,却会出现问题如图结果为50007661(错误结果)。

image-20230228223444201

出现问题的原因是:执行中的进程没有保障,进程之间发生竞争(同一时刻多个进程对内存进行读写操作,发生在多核处理器)

可以利用条件变量实现线程同步机制(进程同步),从而避免资源抢占竞争。

四、非亲缘进程间通信

共享内存实现多进程计算,

  1. 单核不考虑同步关系,可以正常实现
  2. 多核不考虑同步关系,无法正常实现
  3. 需要设置同步关系
  4. 利用条件变量实现进程同步

非亲缘进程之间的通信,

1.通过sleep同步

  • 使用共享内存实现两个非亲缘关系进程(1号进程、2号进程)进行通话
  • 1号进程只输出2号进程在共享内存中输入的数据
  • 2号进程只输出1号进程在共享内存中输入的数据
  • 同步(通过sleep实现同步)
#include "head.h"struct SHM {int flag;//SHM能否读写int type;//第几个进程char mesg[50];//输入的信息
};int main(int argc, char *argv[]) {//1.命令行解析 ./a.out -t 1|2 -m messageint opt;int type;char mesg[50];struct SHM *temp;//用于临时存放准备写入共享内存的数据if (argc != 5) {fprintf(stderr, "Usage : %s -t 1|2 -m message\n", argv[0]);exit(1);}while ((opt = getopt(argc, argv, "t:m:")) != -1) {switch (opt) {case 't':type = atoi(optarg);break;case 'm':strcpy(mesg, optarg);break;default:fprintf(stderr, "Usage : %s -t 1|2 -m message\n", argv[0]);exit(1);}}/***存在问题*为什么直接在switch中使用 temp->type = atoi(optarg); 会出现segmentfault呢?*必须使用临时变量 int type; 来转接数据才不会报错? */temp->type = type;strcpy(temp->mesg, mesg);//2.共享内存的创建与绑定key_t key;int shmid;struct SHM *shmemory = NULL;//2.1申请一块共享内存 将projectId与文件名字转换为 共享内存键值if ((key = ftok("1.shm_my.c", 123)) == -1) {perror("ftok");exit(1);}//2.2根据共享内存对应的key值 和内存大小size 得到共享内存的标识符if ((shmid = shmget(key, sizeof(struct SHM), IPC_CREAT | IPC_EXCL | 0600)) < 0) {if (errno == EEXIST) {//处理重复创建共享内存空间if ((shmid = shmget(key, sizeof(struct SHM), 0600)) < 0) {perror("shmget1");exit(1);}printf("shmemory exist!");} else {perror("shmget2");exit(1);}}//2.3将进程的动态内存空间和 共享内存空间关联if ((shmemory = (struct SHM *)shmat(shmid, NULL, 0)) == (struct SHM *)-1) {perror("shmat");exit(1);}//3.实现非亲缘进程间通信shmemory->flag = 0;//初始状态为允许写入while (1) {if (!shmemory->flag) {printf("<Process%d> : i get shmemory\n", temp->type);sprintf(shmemory->mesg, "<Process%d> : <%s>", temp->type, temp->mesg);//向共享内存中写入内容shmemory->flag = 1;sleep(1);} else {printf("%s\n", shmemory->mesg);//从共享内存中读取 并输出内容shmemory->flag = 0;}}return 0;
}

image-20230301103041419

2.通过条件变量同步

思考:如何通过条件变量以及互斥锁,通过共享内存空间实现多进程同步

  • 一个进程1号进程,作为主要发送进程
  • 多个进程,输出1号进程发送的数据
  • 利用条件变量和互斥锁实现同步

image-20230301110138124

#include "head.h"struct SHM {int type;//第几个进程char mesg[50];//输入的信息pthread_mutex_t mutex;//互斥锁pthread_cond_t cond;//条件变量
};int main(int argc, char *argv[]) {//1.命令行解析 ./a.out -t 1|2int opt;int type;if (argc != 3) {fprintf(stderr, "Usage : %s -t 1|2\n", argv[0]);exit(1);}while ((opt = getopt(argc, argv, "t:")) != -1) {switch (opt) {case 't':type = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -t 1|2\n", argv[0]);exit(1);}}//2.共享内存的创建与绑定key_t key;int shmid;struct SHM *shmemory = NULL;//2.1申请一块共享内存 将projectId与文件名字转换为 共享内存键值if ((key = ftok("2.shm_cond.c", 123)) == -1) {perror("ftok");exit(1);}//2.2根据共享内存对应的key值 和内存大小size 得到共享内存的标识符if ((shmid = shmget(key, sizeof(struct SHM), IPC_CREAT | IPC_EXCL | 0600)) < 0) {if (errno == EEXIST) {//处理重复创建共享内存空间if ((shmid = shmget(key, sizeof(struct SHM), 0600)) < 0) {perror("shmget1");exit(1);}printf("shmemory exist!\n");} else {perror("shmget2");exit(1);}}//2.3将进程的动态内存空间和 共享内存空间关联if ((shmemory = (struct SHM *)shmat(shmid, NULL, 0)) == (struct SHM *)-1) {perror("shmat");exit(1);}//3.实现非亲缘进程间通信if (type == 1) {//让1号进程初始化互斥锁和信号量 并设为共享pthread_mutexattr_t mutex;pthread_condattr_t cond;pthread_mutexattr_init(&mutex);pthread_condattr_init(&cond);pthread_mutexattr_setpshared(&mutex, 1);//设置为共享pthread_condattr_setpshared(&cond, 1);//设置为共享pthread_mutex_init(&shmemory->mutex, &mutex);//初始化锁pthread_cond_init(&shmemory->cond, &cond);//初始化条件}if (type == 1) {while (1) {printf("ok\n");scanf("%[^\n]s", shmemory->mesg); getchar();if (strlen(shmemory->mesg)) pthread_cond_signal(&shmemory->cond);//1号进程写入后通知其他进程}} else {while (1) {pthread_mutex_lock(&shmemory->mutex);//一旦有某个进程拿到了共享内存 则将共享内存上锁pthread_cond_wait(&shmemory->cond, &shmemory->mutex);//共享内存上锁后 等待1号进程写入完成的通知//if (strlen(shmemory->mesg)) printf("<Process%d> : %s\n", type, shmemory->mesg);//将共享内存中写入的数据输出memset(shmemory->mesg, 0, strlen(shmemory->mesg));//清空共享内存pthread_mutex_unlock(&shmemory->mutex);}}return 0;
}

image-20230301171032340

相关文章:

【Linux系统编程】06:共享内存

共享内存 OVERVIEW共享内存一、文件上锁flock二、共享内存1.关联共享内存ftok2.获取共享内存shmget3.绑定共享内存shmat4.绑定分离shmdt5.控制共享内存shmctl三、亲缘进程间通信1.共享内存写入与读取2.共享内存解绑与删除3.共享内存综合四、非亲缘进程间通信1.通过sleep同步2.通…...

【专项】112. 路径总和

112. 路径总和 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点 …...

【数据结构】堆排序

堆是一种叫做完全二叉树的数据结构&#xff0c;可以分为大根堆&#xff0c;小根堆&#xff0c;而堆排序就是基于这种结构而产生的一种程序算法。大堆&#xff1a;每个节点的值都大于或者等于他的左右孩子节点的值小堆&#xff1a;每个结点的值都小于或等于其左孩子和右孩子结点…...

论文阅读笔记《GAMnet: Robust Feature Matching via Graph Adversarial-Matching Network》

核心思想 本文提出一种基于图对抗神经网络的图匹配算法&#xff08;GAMnet&#xff09;,使用图神经网络作为生成器分别生成源图和目标图的节点的特征&#xff0c;并用一个多层感知机作为辨别器来区分两个特征是否来自同一个图&#xff0c;通过对抗训练的办法提高生成器特征提取…...

数据安全—数据完整性校验

1、数据安全保障三要素即 保密性 完整性、可用性机密性&#xff1a;要求数据不被他人轻易获取&#xff0c;需要进行数据加密。完整性&#xff1a;要求数据不被他人随意修改&#xff0c;需要进行签名技术可用性&#xff1a;要求服务不被他人恶意攻击&#xff0c;需要进行数据校验…...

Java 最小路径和

最小路径和中等给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。说明&#xff1a;每次只能向下或者向右移动一步。示例 1&#xff1a;输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]]输出&…...

Flask+VUE前后端分离的登入注册系统实现

首先Pycharm创建一个Flask项目&#xff1a; Flask连接数据库需要下载的包&#xff1a; pip install -U flask-cors pip install flask-sqlalchemy Flask 连接和操作Mysql数据库 - 王滚滚啊 - 博客园 (cnblogs.com) sqlAlchemy基本使用 - 简书 (jianshu.com) FlaskVue前后端分…...

【Go】用Go在命令行输出好看的表格

用Go在命令行输出好看的表格前言正文生成Table表头设置插入行表格标题自动标号单元格合并列合并行合并样式设置居中设置数字自动高亮标红完整Demo代码结语前言 最近在写一些运维小工具&#xff0c;比如批量进行ping包的工具&#xff0c;实现不困难&#xff0c;反正就是ping&am…...

怎么处理消息重发的问题?

消息队列在消息传递的过程中&#xff0c;如果出现传递失败的情况&#xff0c;发送方会重试&#xff0c;在重试的过程中&#xff0c;可能会产生重复的消息。 消息重复的情况必然存在 关于传递消息时能够提供的服务质量标准&#xff0c;MQTT协议给出了三种不同的标准&#xff1…...

JVM 运行时数据区(数据区组成表述,程序计数器,java虚拟机栈,本地方法栈)

JVM 运行时数据区JVM 运行时数据区3.1运行时的数据区组成概述3.1.1程度计数器3.1.2java虚拟机栈3.1.3本地方法栈3.1.4java堆3.1.5方法区3.2程序计数器3.3java虚拟机栈3.4本地方法栈JVM 运行时数据区 堆,方法区(元空间) 主要用来存放数据 是线程共享的. 程序计数器,本地方法栈…...

Oracle ASM磁盘组配置、日常运维、故障处理等操作资料汇总

ASM&#xff08;自动存储管理&#xff09;在数据库中是非常重要的组成部分&#xff0c;它可以为磁盘提供统一的存储管理、提高磁盘访问的性能和可用性、简化管理复杂度&#xff0c;从而为数据库的运行提供更好的支持。这里就为大家整理了墨天轮数据社区上一些ASM相关基础知识、…...

java对象的创建与内存分配机制

文章目录对象的创建与内存分配机制对象的创建类加载检查分配内存初始化零值设置对象头指向init方法其他&#xff1a;指针压缩对象内存分配对象在栈上分配对象在Eden区中分配大对象直接分配到老年代长期存活的对象进入老年代对象动态年龄判断老年代空间分配担保机制对象的内存回…...

本地存储localStorage、sessionStorage

目录 一、localStorage 二、sessionStorage 三、本地存储处理复杂数据 一、localStorage 介绍 &#xff08;1&#xff09;数据存储在用户浏览器中 &#xff08;2&#xff09;设置、读取方便、甚至页面刷新不会丢失数据 &#xff08;3&#xff09;容量较大&#xff0c;se…...

JavaSE: 网络编程

1.1 概述java程序员面对统一的网络编程环境B/S 架构 和 C/S架构1.2 网络通信的两个要素通信双方的地址&#xff1a;ip 端口号网络通信协议&#xff1a;TCP/IP协议&#xff08;事实上的国际规则&#xff09;、OSI模型&#xff08;理想化&#xff09;1.3 Inet Address本地回环地…...

计算机图形学09:二维观察之点的裁剪

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、二维观察基本…...

2023Java 并发编程面试题

Java 并发编程 1、在 java 中守护线程和本地线程区别&#xff1f; java 中的线程分为两种&#xff1a;守护线程&#xff08;Daemon&#xff09;和用户线程&#xff08;User&#xff09;。任何线程都可以设置为守护线程和用户线程&#xff0c;通过方法Thread.setDaemon(boolon…...

CAD如何绘制A0/A1/A2/A3/A4图框?

在CAD制图时&#xff0c;设计师一般会使用企业的定制图框模板或者个人的特色图框模板&#xff0c;让设计方案更加标准化、规范化。对于新人设计师而言&#xff0c;完成CAD制图已经非常头疼了&#xff0c;图框的绘制更是手忙脚乱。那么是否有更加高效的方式来完成A0、A1、A2、A3…...

R 安装 “umap-learn“ python 包

首先需要在R中下载并读取reticulate包&#xff0c;该包提供了一系列R-Python的交互式命令由于之前在电脑中通过三个方式安装了Python&#xff1a;直接安装 Python 3.10安装Anaconda&#xff0c;携带3.9安装 Miniconda&#xff0c;又是另外一个版本的Python版本各不相同&#xf…...

测试同学如何快速开发测试平台?

转眼已经好几个月没有发表什么文章了&#xff0c;因为疫情原因&#xff0c;大家工作都不怎么顺利&#xff0c;没有什么心情。再者&#xff0c;最近一直在搞移动端精准测试的项目&#xff0c;有太多技术难点需要攻克。从各个网站上都找不到解决方案&#xff0c;只能不断地尝试&a…...

【程序员接口百宝箱】免费常用API接口

一、短信发送 短信的应用可以说是非常的广泛了&#xff0c;短信API也是当下非常热门的API~ 短信验证码&#xff1a;可用于登录、注册、找回密码、支付认证等等应用场景。支持三大运营商&#xff0c;3秒可达&#xff0c;99.99&#xff05;到达率&#xff0c;支持大容量高并发。…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...