C语言:高级并发操作(信号)

引言

同步和异步的使用。

异步事件处理的两种方法:查询法、通知法。(单核机器不存在异步)

一、信号

1. 信号的概念

信号是软件中断。信号的响应依赖于中断。中断是底层硬件的机制。

2. signal函数

kill -l命令查看所有的信号。1 -31 属于标准信号 34 - 64属于实时信号


       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
展示形式为: void (* signal(int signum, void (*func)(int) )    ) (int )

信号会打断阻塞的系统调用。

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void mySig()
{write(1,".",1);
}int main()
{signal(SIGINT,SIG_IGN);//运行过程中忽略这个信号signal(SIGINT,mySig);int i;for(i = 0;i<10;i++){write(1,"*",1);sleep(1);}return 0;
}

重构之前的mycpy.c  对于open  write read 函数增加出现系统调用打断的行为判断

 mycpy.c

#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>#define BUF_SIZE    1024*100
int main(int argc,char **argv)
{int fps,fpd;int ret;char buf[BUF_SIZE];int len,pos;if(argc <3){fprintf(stderr,"Usage:%s <src_file> <dest_file>\n",argv[0]);exit(1);}do{fps = open(argv[1],O_RDONLY);if(fps <0){if(errno != EINTR){perror("open");exit(1);}}}while(fps<0);do{fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600 );if(fpd <0){if(errno != EINTR){close(fps);perror("open");exit(1);}}}while(fpd<0);while(1){len = read(fps,buf,BUF_SIZE);if(len < 0){if(errno == EINTR)continue;perror("read()");break;}if(len ==0)break;pos = 0;while(len > 0){ret = write(fpd,buf+pos,len);if(ret <0){if(errno == EINTR)continue;perror("write()");exit(1);}pos += ret;len-=ret;}}close(fpd);close(fps);return 0;
}

3. 信号的不可靠

标准的信号会丢失,实时信号不会丢失。指的是信号的行为不可靠。(第一次调用还未结束,第二次调用就开始了)

4. 可重入函数

第一次调用还未结束,第二次调用不会出错叫可重入函数(reentrant)。所有的系统调用都是可重入的,一部分库函数也是可重入的,比如说:memcpy rand_r localtime_r   memove

5. 信号的响应过程

mask(信号屏蔽字,32位一般情况都是1)pending(32位 位图,初始都为0),收到信号后pending位置1,内核在kernal态进入到user态时,会用mask值 & pending 获取发生的信号,将mask与pending全部置为0,执行函数,执行完毕将mask位置1。

信号从收到到响应有一个不可避免的延迟。
思考:
如何忽略掉一个信号?  将mask位置0
标准信号为什么会丢失?  在执行信号时,pending位置多次1,此时mask位是0
标准信号的响应没有严格的顺序。
不能从信号处理函数中随意的往外跳。(sigsetjmp,siglongjmp)

6. 相关常用函数

kill

发送一个信号到一个进程或进程组

raise

给当前进程或者线程发送一个信号

alarm

指定秒数,发送SIGALRM信号。默认alarm会杀掉当前程序。alarm不能实现多任务的计时器,只能执行最后一次设置的计时器。

alarm.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void mySig()
{write(1,".",1);
}int main()
{alarm(5);alarm(2);alarm(10);while(1)pause();
#if 0signal(SIGALRM,mySig);int i;for(i = 0;i<10;i++){write(1,"*",1);alarm(1);sleep(1);}
#endif return 0;
}

5秒进行计数,不用alarm实现

5sec.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>int main()
{time_t end;long long  count = 0;end = time(NULL)+5;while(time(NULL)<= end){count++;}printf("count = %lld \n",count);return 0;
}

 使用alarm实现

5sec_signal.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>static int loop = 1;void alrm_handler(int num)
{loop = 0;
}int main()
{signal(SIGALRM,alrm_handler);alarm(5);long long  count=0;while(loop){count++;}printf("count = %lld \n",count);return 0;
}

gcc -S 5sec_signal.c 会产生 5sec_singal.s的汇编文件  

使用-O1优化之后,程序会一直执行下去,因为程序中while中没有用到loop,优化后loop的值会直接从寄存器取程序while会一直成立,volatile关键字告诉程序要去真正的地址取数据,不要从内存获取

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>static volatile int loop = 1;  //从真实的空间去取数据void alrm_handler(int num)
{loop = 0;
}int main()
{signal(SIGALRM,alrm_handler);alarm(5);long long  count=0;while(loop){count++;}printf("count = %lld \n",count);return 0;
}
mycat.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>#define BUF_SIZE    1024*100
int main(int argc,char **argv)
{int fps,fpd=1;int ret;char buf[BUF_SIZE];int len,pos;if(argc <2){fprintf(stderr,"Usage:%s <src_file> <dest_file>\n",argv[0]);exit(1);}do{fps = open(argv[1],O_RDONLY);if(fps <0){if(errno != EINTR){perror("open");exit(1);}}}while(fps<0);while(1){len = read(fps,buf,BUF_SIZE);if(len < 0){if(errno == EINTR)continue;perror("read()");break;}if(len ==0)break;pos = 0;while(len > 0){ret = write(fpd,buf+pos,len);if(ret <0){if(errno == EINTR)continue;perror("write()");exit(1);}pos += ret;len-=ret;}}close(fps);return 0;
}

 实例:使用单一计时器,构造一组函数实现,任意数量的计时器。        

pause

等待一个信号来打断他,让程序不在忙等(while函数)

abort

给当前进程发送一个SIGABRT信号,并且产生一个core文件

system

调用shell完成shell命令。           execl("/bin/sh", "sh", "-c", command, (char *) NULL);

如果在信号中使用,需要block 这个SIGCHLD 信号,并且 忽略SIGINT SIGQUIT信号。

sleep

某些平台 用alarm+pause封装 ,当前平台用nanolseep封装,考虑到移植最好不要使用sleep.可以使用usleep,select 

7. 信号集

  int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);
 

8. 信号屏蔽字mask/pending集处理

 /* Prototype for the glibc wrapper function

glibc包装器函数的原型

*/
       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

       /* Prototype for the underlying system call

底层系统调用的原型

*/
       int rt_sigprocmask(int how, const kernel_sigset_t *set,
                          kernel_sigset_t *oldset, size_t sigsetsize);

       /* Prototype for the legacy system call (deprecated)

旧系统调用的原型(已弃用)

*/
       int sigprocmask(int how, const old_kernel_sigset_t *set,
                       old_kernel_sigset_t *oldset);

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void mySig()
{write(1,".",1);
}int main()
{signal(SIGINT,mySig);int i;sigset_t set,old_set;sigemptyset(&set);sigaddset(&set,SIGINT);for(i = 0;i<1000;i++){sigprocmask(SIG_BLOCK,&set,NULL); //堵塞 SIGINT信号for(int j=0;j<5;j++){write(1,"*",1);sleep(1);  // 信号可以打断堵塞 的系统调用}write(1,"\n",1);sigprocmask(SIG_UNBLOCK,&set,NULL); //解除堵塞信号}return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void mySig()
{write(1,".",1);
}int main()
{signal(SIGINT,mySig);int i;sigset_t set,old_set;sigemptyset(&set);sigaddset(&set,SIGINT);for(i = 0;i<1000;i++){sigprocmask(SIG_BLOCK,&set,&old_set); // 保存之前的状态for(int j=0;j<5;j++){write(1,"*",1);sleep(1);  // 信号可以打断堵塞 的系统调用}write(1,"\n",1);sigprocmask(SIG_SETMASK,&old_set,NULL); //回复当前的状态}return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void mySig()
{write(1,".",1);
}int main()
{signal(SIGINT,mySig);int i;sigset_t set,old_set,save_set;sigemptyset(&set);sigaddset(&set,SIGINT);sigprocmask(SIG_UNBLOCK,&set,&save_set);// 模块话编程,先保存之前的状态。for(i = 0;i<1000;i++){sigprocmask(SIG_BLOCK,&set,NULL);for(int j=0;j<5;j++){write(1,"*",1);sleep(1);  // 信号可以打断堵塞 的系统调用}write(1,"\n",1);sigprocmask(SIG_UNBLOCK,&old_set,NULL); }sigprocmask(SIG_SETMASK,&save_set,NULL); //结束后,恢复之前保存的状态。return 0;
}

9. 扩展

sigsuspend()

实现信号驱动程序。

susp.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void mySig()
{write(1,".",1);
}int main()
{signal(SIGINT,mySig);int i;sigset_t set,old_set,save_set;sigemptyset(&set);sigaddset(&set,SIGINT);sigprocmask(SIG_UNBLOCK,&set,&save_set);// 模块话编程,先保存之前的状态。sigprocmask(SIG_BLOCK,&set,&old_set);for(i = 0;i<1000;i++){//     sigprocmask(SIG_BLOCK,&set,&old_set);for(int j=0;j<5;j++){write(1,"*",1);sleep(1);  // 信号可以打断堵塞 的系统调用}write(1,"\n",1);sigsuspend(&old_set);
/* sigsuspend的原子操作sigset_t tmpset;sigprocmask(SIG_SETMASK,&old_set,&tmpset); //解除阻塞 pause(); //响应信号sigprocmask(SIG_SETMASK,&tmpset,NULL); //重新恢复信号。*/}sigprocmask(SIG_SETMASK,&save_set,NULL); //结束后,恢复之前保存的状态。return 0;
}

sigaction()

用于替换signal,signal有使用上的缺陷。
       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

    struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

第三个参数来源于:

       #include <ucontext.h>

       int getcontext(ucontext_t *ucp);
       int setcontext(const ucontext_t *ucp);
 

struct sigaction sa;
      sa.sa_handler = daemon_exit; //指定要执行的函数
      sigemptyset(&sa.sa_mask);
      sigaddset(&sa.sa_mask,SIGINT)
      sigaddset(&sa.sa_mask,SIGQUIT)
      sigaddset(&sa.sa_mask,SIGTERM)
      sa.sa_flag = 0;
      sigaction(SIGINT,&sa,NULL);

while true ;do kill -ALRM 121812;done;SHELL脚本

重新更新mytbf,不响应终端的信号。

mytbf.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include "mytbf.h"struct mytbf_st
{int cps;int burst;int token;int pos;};
static struct mytbf_st* job[MYTBF_MAX];
static int inited = 0;typedef void (*sighandler_t)(int);struct sigaction alrm_sa_save;static int get_free_pos(void)
{for(int i=0;i< MYTBF_MAX;i++){if(job[i]==NULL)return i;}return -1;
}static void alrm_action(int num,siginfo_t*infop,void *unsed)
{if(!infop->si_code == SI_KERNEL) //响应kernal来的信号,一般从程序中发出就是kernal来的。return;for(int i=0;i<MYTBF_MAX;i++){if(job[i] != NULL){job[i]->token += job[i]->cps;if(job[i]->token >job[i]->burst ){job[i]->token = job[i]->burst;}}}}static void module_unload()
{sigaction(SIGALRM,&alrm_sa_save,NULL);struct itimerval itv;itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = 0;itv.it_value.tv_usec = 0;setitimer(ITIMER_REAL,&itv,NULL);for(int i=0;i<MYTBF_MAX;i++)free(job[i]);
}static void module_load()
{struct sigaction sa;sa.sa_sigaction  = alrm_action; sigemptyset(&sa.sa_mask);sa.sa_flags = SA_SIGINFO; //表示传入的是三参的形式sigaction(SIGALRM,&sa,&alrm_sa_save);struct itimerval itv;itv.it_interval.tv_sec = 1;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 0;setitimer(ITIMER_REAL,&itv,NULL);atexit(module_unload);
}mytbf_t* mytbf_init(int cps ,int burst)  //C语言中,void*可以赋值给任何类型的指针,任何类型的指针也都可以赋值给void*
{struct mytbf_st*me;int pos;if(inited == 0){module_load();inited = 1;}pos = get_free_pos();if(pos < 0)return NULL;me = malloc(sizeof(*me));if(me == NULL)return NULL;me->cps = cps;me->burst = burst;me->token = 0;me->pos = pos;job[pos] = me;return me;}
static int min(int token,int size)
{if(token> size)return size;return token;
}
int mytbf_fetchtoken(mytbf_t*ptr,int size)  //获取token
{if(size <= 0)return -EINVAL;  //参数非法struct mytbf_st*me = ptr;while(me->token <= 0 )  //token为空就等待pause();//  int n = min(me->token,size);int n = (me->token>size?size:me->token);me->token -= n;return n;
}int mytbf_returntoken(mytbf_t*ptr,int size)  //返还token
{if(size<=0)return -EINVAL;struct mytbf_st*me = ptr;me->token+= size;if(me->token > me->burst)me->token  = me->burst;return size;
}int mytbf_destroy(mytbf_t*ptr)
{struct mytbf_st *me;me = ptr;job[me->pos] = NULL;free(ptr);return 0;}

setitimer()

 #include <sys/time.h>

       int getitimer(int which, struct itimerval *curr_value);
       int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value)

比alarm时间更加的准确

           struct itimerval {
               struct timeval it_interval; /* Interval for periodic timer */
               struct timeval it_value;    /* Time until next expiration */
           };

           struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };


slowcat.c
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>#define CPS 100
#define BUF_SIZE    CPSstatic volatile int loop = 0;static void alrm_handler(int num)
{
//     alarm(1);loop = 1;
}int main(int argc,char **argv)
{int fps,fpd=1;int ret;char buf[BUF_SIZE];int len,pos;if(argc <2){fprintf(stderr,"Usage:%s \n",argv[0]);exit(1);}signal(SIGALRM,alrm_handler);
//    alarm(1);struct itimerval itv;itv.it_interval.tv_sec = 1;itv.it_interval.tv_usec = 0;itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 1;if(setitimer(ITIMER_REAL,&itv,NULL) <0){fprintf(stderr,"setitimer:%s",strerror(errno));}do{fps = open(argv[1],O_RDONLY);if(fps <0){if(errno != EINTR){perror("open");exit(1);}}}while(fps<0);while(1){//while(!loop);while(loop == 0)pause();   //用于等待一个打断的到来。不加则是忙等。loop = 0;while(1){len = read(fps,buf,BUF_SIZE);if(len < 0){if(errno == EINTR)continue;perror("read()");break;}break;}if(len ==0)break;pos = 0;while(len > 0){ret = write(fpd,buf+pos,len);if(ret <0){if(errno == EINTR)continue;perror("write()");exit(1);}pos += ret;len-=ret;}}close(fps);return 0;
}

  int sigpending(sigset_t *set); 不清楚使用场景。
 

10. 实时信号

标准信号会丢失,实时信号不会丢失,需要排队,响应有一个顺序。

SIGUSR1 SIGUSR2是预留的信号。

susp_rt.c

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>#define MYRTSIG (SIGRTMIN+6)void mySig()
{write(1,".",1);
}int main()
{signal(MYRTSIG,mySig);int i;sigset_t set,old_set,save_set;sigemptyset(&set);sigaddset(&set,MYRTSIG);sigprocmask(SIG_UNBLOCK,&set,&save_set);// 模块话编程,先保存之前的状态。sigprocmask(SIG_BLOCK,&set,&old_set);for(i = 0;i<1000;i++){//     sigprocmask(SIG_BLOCK,&set,&old_set);for(int j=0;j<5;j++){write(1,"*",1);sleep(1);  // 信号可以打断堵塞 的系统调用}write(1,"\n",1);sigsuspend(&old_set);}sigprocmask(SIG_SETMASK,&save_set,NULL); //结束后,恢复之前保存的状态。return 0;
}

kill -40 pid 杀掉信号,可以使程序继续运行,切发送多次信号都会执行。排队次数用ulimit -i查看。 

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

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

相关文章

使用qt creator配置msvc环境(不需要安装shit一样的宇宙第一IDE vs的哈)

1. 背景 习惯使用Qt编程的童鞋&#xff0c;尤其是linux下开发Qt的童鞋一般都是使用qt creator作为首选IDE的&#xff0c;通常在windows上使用Qt用qt creator作为IDE的话一般编译器有mingw和msvc两种&#xff0c;使用mingw版本和在linux下的方式基本上一样十分简单&#xff0c;不…

如何在Ubuntu环境下使用加速器配置Docker环境

一、安装并打开加速器 这个要根据每个加速器的情况来安装并打开&#xff0c;一般是会开放一个代理端口&#xff0c;比如1087 二、安装Docker https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script 三、配置Docker使用加速器 3.1 修改配置…

UE5 04-重新加载当前场景

给关卡加一个淡出的效果 给关卡加一个淡入的效果, 这个最好放置在Player 上,这样切关卡依然有这个效果

防火墙基础及登录(华为)

目录 防火墙概述防火墙发展进程包过滤防火墙代理防火墙状态检测防火墙UTM下一代防火墙&#xff08;NGFW&#xff09; 防火墙分类按物理特性划分软件防火墙硬件防火墙 按性能划分百兆级别和千兆级别 按防火墙结构划分单一主机防火墙路由集成式防火墙分布式防火墙 华为防火墙利用…

Qt 关于字节流可否嵌套的一个实验

1.可否嵌套方案 1.1 概要 * 需求&#xff1a;数据头数据体校验码数据尾 * 校验码的生成只与数据体相关 * 可否通过QByteArray的嵌套实现这个功能呢&#xff0c; * 最终失败&#xff1a; * 理由一个char*的整体内存是无法再头部插入输入的。 * 那么只能靠生成校验码的时候…

creature_equip_template

creature_equip_template CreatureID 链接 creature_template.entry ID creature 装备模板编号 一个 creature entry 可以有多个装备模板如有多个装备模板&#xff0c;从1开始依次递增1 ItemID1 - ItemID3 装备模板使用的装备id&#xff0c;取值参见 ItemSparse.db2 | Item.db2…

什么是数据分析?数据分析如何创造企业发挥价值?

数据分析是在具体的业务场景下&#xff0c;如何借助工具&#xff0c;通过数据解决问题的思路 数据底层的四大优势 1.可反复读取和使用 2.客观 3.量化 4.机器可处理 使用数据指导业务&#xff0c;基于数据量化生产 只要是基于量化的信息提升生产力&#xff0c;就是数据分析&a…

MySQL-18-mysql source 执行 sql 文件时中文乱码

拓展阅读 MySQL 00 View MySQL 01 Ruler mysql 日常开发规范 MySQL 02 truncate table 与 delete 清空表的区别和坑 MySQL 03 Expression 1 of ORDER BY clause is not in SELECT list,references column MySQL 04 EMOJI 表情与 UTF8MB4 的故事 MySQL 05 MySQL入门教程&a…

centos7|操作系统|升级openssl-1.0.2k到openssl-3.3.0

一、 前言&#xff1a; opensssl是什么软件&#xff1f;openssl的版本是怎样的&#xff1f;为什么需要升级openssl&#xff1f;如何升级openssl&#xff1f; 1、openssl是一个什么样软件&#xff1f; OpenSSL是一个开源的安全套接字层&#xff08;Secure Sockets Layer&…

MySQL8.0在windows下的下载安装及详细使用

下载mysql8.0二进制包 下载地址&#xff1a;MySQL :: Download MySQL Community Server 编辑my.ini配置文件 解压二进制包&#xff0c;新建/编辑my.ini配置文件(如果不存在则新建) [client] #客户端设置&#xff0c;即客户端默认的连接参数 # 设置mysql客户端连接服务端时…

Canvas:掌握颜色线条与图像文字设置

想象一下&#xff0c;用几行代码就能创造出如此逼真的图像和动画&#xff0c;仿佛将艺术与科技完美融合&#xff0c;前端开发的Canvas技术正是这个数字化时代中最具魔力的一环&#xff0c;它不仅仅是网页的一部分&#xff0c;更是一个无限创意的画布&#xff0c;一个让你的想象…

回溯 | Java | LeetCode 39, 40, 131 做题总结

Java Arrays.sort(数组) //排序 不讲究顺序的解答&#xff0c;都可以考虑一下排序是否可行。 39. 组合总和 错误解答 在写的时候需要注意&#xff0c;sum - candidates[i];很重要&#xff0c;也是回溯的一部分。 解答重复了。是因为回溯的for循环理解错了。 class Solutio…

Python StrEnum: 基本概念和使用场景

Python StrEnum: 基本概念和使用场景 什么是StrEnum?基本用法使用场景1. 配置选项2. API状态码3. 数据验证 注意事项结论 在Python编程中,枚举类型是一种非常有用的工具,可以用来定义一组命名常量。Python 3.4引入了Enum类,而在Python 3.11中,我们迎来了一个新的枚举类型 ——…

极域?去!

本文由Jzwalliser原创&#xff0c;发布在CSDN平台上&#xff0c;遵循CC 4.0 BY-SA协议。 因此&#xff0c;若需转载/引用本文&#xff0c;请注明作者并附原文链接&#xff0c;且禁止删除/修改本段文字。 违者必究&#xff0c;谢谢配合。 个人主页&#xff1a;blog.csdn.net/jzw…

使用OpenCV与PySide(PyQt)的视觉检测小项目练习

OpenCV 提供了丰富的图像处理和计算机视觉功能&#xff0c;可以实现各种复杂的图像处理任务&#xff0c;如目标检测、人脸识别、图像分割等。 PyQt(或PySide)是一个创建GUI应用程序的工具包&#xff0c;它是Python编程语言和Qt库的成功融合。Qt库是最强大的GUI库之一。Qt的快速…

【开放集目标检测】Grounding DINO

一、引言 论文&#xff1a; Grounding DINO: Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection 作者&#xff1a; IDEA 代码&#xff1a; Grounding DINO 注意&#xff1a; 该算法是在Swin Transformer、Deformable DETR、DINO基础上…

逆变器学习笔记(三)

DCDC电源芯片外围器件选型_dcdc的comp补偿-CSDN博客、 1.芯片的COMP引脚通常用于补偿网络&#xff1a; 芯片的COMP引脚通常用于补偿网络&#xff0c;在控制环路中发挥重要作用。COMP引脚接电容和电阻串联接地&#xff0c;主要是为了稳定控制环路、调整环路响应速度和滤波噪声…

Java 变量命名规则

在Java中&#xff0c;变量命名是一种重要的编码规范&#xff0c;良好的命名可以提高代码的可读性和维护性。以下是Java变量命名的规则和建议&#xff1a; ### Java变量命名规则&#xff1a; 1. **合法字符**&#xff1a; - 变量名可以包含字母、数字、美元符号 $ 和下划线…

java Lock接口

在 Java 中&#xff0c;Lock 接口的实现类ReentrantLock 类提供了比使用 synchronized 方法和代码块更广泛的锁定机制。 简单示例&#xff1a; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExampl…

Kappa架构

1.Kappa架构介绍 Kappa架构由Jay Kreps提出&#xff0c;不同于Lambda同时计算和批计算并合并视图&#xff0c;Kappa只会通过流计算一条的数据链路计算并产生视图。Kappa同样采用了重新处理事件的原则&#xff0c;对于历史数据分析类的需求&#xff0c;Kappa要求数据的长期存储能…