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

pg_cron优化案例--terminate pg_cron launcher可自动拉起

场景

在PostgreSQL中我们可以使用pg_cron来实现数据库定时任务

我有一个select 1的定时任务,每分钟触发一次

testdb=# select * from cron.job ;jobid |  schedule   | command  | nodename  | nodeport | database | username | active |    jobname    
-------+-------------+----------+-----------+----------+----------+----------+--------+---------------2 | */1 * * * * | select 1 | localhost |     1142 | testdb   | admin    | t      | manual active
(1 row)
testdb=#

从执行记录来看从某个时刻开始不执行了

testdb=# select * from cron.job_run_details where jobid='2';jobid | runid | job_pid | database | username | command  |  status   | return_message |          start_time           |           end_time            
-------+-------+---------+----------+----------+----------+-----------+----------------+-------------------------------+-------------------------------2 |     3 |   29616 | testdb   | admin    | select 1 | succeeded | 1 row          | 2023-02-08 22:37:00.014232+08 | 2023-02-08 22:37:00.015855+082 |     4 |   29772 | testdb   | admin    | select 1 | succeeded | 1 row          | 2023-02-08 22:38:00.010803+08 | 2023-02-08 22:38:00.012029+082 |     5 |   29995 | testdb   | admin    | select 1 | succeeded | 1 row          | 2023-02-08 22:39:00.013508+08 | 2023-02-08 22:39:00.015362+08
(3 rows)testdb=# select now();now             
-----------------------------2023-02-13 11:11:10.7302+08
(1 row)testdb=# 

从日志来看是pg_cron launcher进程shutdown了,pg_cron launcher是job的调度进程,当它停止了,任务也就不调度了。

2023-02-08 22:17:04.788 CST,,,25712,,63e3aee0.6470,1,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"pg_cron scheduler started",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:37:00.008 CST,,,25712,,63e3aee0.6470,2,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 starting: select 1",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:37:00.017 CST,,,25712,,63e3aee0.6470,3,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 completed: 1 row",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:38:00.006 CST,,,25712,,63e3aee0.6470,4,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 starting: select 1",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:38:00.013 CST,,,25712,,63e3aee0.6470,5,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 completed: 1 row",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:39:00.006 CST,,,25712,,63e3aee0.6470,6,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 starting: select 1",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:39:00.017 CST,,,25712,,63e3aee0.6470,7,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"cron job 2 completed: 1 row",,,,,,,,,"","pg_cron launcher",,0
2023-02-08 22:39:54.618 CST,,,25712,,63e3aee0.6470,8,,2023-02-08 22:17:04 CST,2/0,0,LOG,00000,"pg_cron scheduler shutting down",,,,,,,,,"","pg_cron launcher",,0

总的来说就是实例未停止的情况下,pg_cron launcher shutdown导致job未调度。

分析

从代码来看当pg_cron launcher 收到SIGTERM后退出时会打印"pg_cron scheduler shutting down"这条日志。

/** PgCronLauncherMain is the main entry-point for the background worker* that performs tasks.*/
void
PgCronLauncherMain(Datum arg)
{MemoryContext CronLoopContext = NULL;struct rlimit limit;/* Establish signal handlers before unblocking signals. */pqsignal(SIGHUP, pg_cron_sighup);pqsignal(SIGINT, SIG_IGN);pqsignal(SIGTERM, pg_cron_sigterm);/* We're now ready to receive signals */BackgroundWorkerUnblockSignals();/* 省略部分代码行 *//* 当未接收到SIGTERM时一直在while循环中 */while (!got_sigterm){/* 省略部分代码行 */	}/* 那么当接收到SIGTERM时,打印日志并exit(0)退出 */ereport(LOG, (errmsg("pg_cron scheduler shutting down")));proc_exit(0);
}

由此得知,在实例shutdown或者使用select pg_terminate_backend() 终止pg_cron launcher这两种场景下会打印对应的日志,看起来我这个实例的pg_cron launcher就是被 pg_terminate_backend()函数终止了。

相比其他bgworker比如logical replication launcher,当进程被pg_terminate_backend() 终止后,postmaster会检测到并且再次拉起该进程。为什么pg_cron launcher被SIGTERM终止后,没有被再次拉起呢?

这里其实就在于对进程退出的处理不同。

可以看到PgCronLauncherMain中当接收到SIGTERM时,打印日志后proc_exit(0)退出。

而logical replication launcher这里的实现,SIGTERM注册的处理函数是die,当接收到SIGTERM信号后除了setlatch wakeup进程,还会将Interrupt的全局flag置为ture 进入CHECK_FOR_INTERRUPTS()中执行对应的报错逻辑,最终进程会走FATAL报错退出,可以看到errfinish中对于FATAL错误的处理就是调用proc_exit(1)退出进程。

注册信号处理函数

/** Main loop for the apply launcher process.*/
void
ApplyLauncherMain(Datum main_arg)
{/* 省略部分代码行 *//* Establish signal handlers. */pqsignal(SIGHUP, SignalHandlerForConfigReload);/* 注册SIGTERM处理函数为die */pqsignal(SIGTERM, die);BackgroundWorkerUnblockSignals();/* 省略部分代码行 *//* Enter main loop */for (;;){/* 省略部分代码行 *//* Wait for more work. */rc = WaitLatch(MyLatch,WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,wait_time,WAIT_EVENT_LOGICAL_LAUNCHER_MAIN);if (rc & WL_LATCH_SET){   /* 当进程wakeup,则检测是否发生INTERRUPT */ResetLatch(MyLatch);CHECK_FOR_INTERRUPTS();}if (ConfigReloadPending){ConfigReloadPending = false;ProcessConfigFile(PGC_SIGHUP);}}/* Not reachable */
}

ProcessInterrupts中对bgworker的处理

/** ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro** If an interrupt condition is pending, and it's safe to service it,* then clear the flag and accept the interrupt.  Called only when* InterruptPending is true.** Note: if INTERRUPTS_CAN_BE_PROCESSED() is true, then ProcessInterrupts* is guaranteed to clear the InterruptPending flag before returning.* (This is not the same as guaranteeing that it's still clear when we* return; another interrupt could have arrived.  But we promise that* any pre-existing one will have been serviced.)*/
void
ProcessInterrupts(void)
{/* OK to accept any interrupts now? */if (InterruptHoldoffCount != 0 || CritSectionCount != 0)return;InterruptPending = false;if (ProcDiePending){/* 省略部分代码行 */else if (IsBackgroundWorker)ereport(FATAL,(errcode(ERRCODE_ADMIN_SHUTDOWN),errmsg("terminating background worker \"%s\" due to administrator command",MyBgworkerEntry->bgw_type)));/* 省略部分代码行 */}/* 省略部分代码行 */
}

errfinish中对于FATAL错误的处理

/** errfinish --- end an error-reporting cycle** Produce the appropriate error report(s) and pop the error stack.** If elevel, as passed to errstart(), is ERROR or worse, control does not* return to the caller.  See elog.h for the error level definitions.*/
void
errfinish(const char *filename, int lineno, const char *funcname)
{/* 省略部分代码行 *//** Perform error recovery action as specified by elevel.*/if (elevel == FATAL){/** For a FATAL error, we let proc_exit clean up and exit.** If we just reported a startup failure, the client will disconnect* on receiving it, so don't send any more to the client.*/if (PG_exception_stack == NULL && whereToSendOutput == DestRemote)whereToSendOutput = DestNone;/** fflush here is just to improve the odds that we get to see the* error message, in case things are so hosed that proc_exit crashes.* Any other code you might be tempted to add here should probably be* in an on_proc_exit or on_shmem_exit callback instead.*/fflush(stdout);fflush(stderr);/** Let the statistics collector know. Only mark the session as* terminated by fatal error if there is no other known cause.*/if (pgStatSessionEndCause == DISCONNECT_NORMAL)pgStatSessionEndCause = DISCONNECT_FATAL;/** Do normal process-exit cleanup, then return exit code 1 to indicate* FATAL termination.  The postmaster may or may not consider this* worthy of panic, depending on which subprocess returns it.*/proc_exit(1);}/* 省略部分代码行 */
}

在C语言中exit(0)表示的是程序正常退出,exit(1)则为异常退出。

当子进程退出时,会向父进程Postmaster发送SIGCHLD信号,postmaster注册了这个信号的信号处理函数reaper,通过waitpid去回收子进程,并做一些处理。
那么在Postmaster进程的serverLoop主循环里会检测子进程状态判断是否需要拉起子进程,以bgworker这种为例,在maybe_start_bgworkers里获取BackgroundWorkerList读取对应bgworker信息,
如果对应的bgworker是正常退出的,那么则不在这个列表中,因此不会拉起。
当bgworker是异常退出,对应信息会保留在BackgroundWorkerList里,但是当前的pid为0,因此就会将其拉起。

maybe_start_bgworkers的处理逻辑

/** If the time is right, start background worker(s).** As a side effect, the bgworker control variables are set or reset* depending on whether more workers may need to be started.** We limit the number of workers started per call, to avoid consuming the* postmaster's attention for too long when many such requests are pending.* As long as StartWorkerNeeded is true, ServerLoop will not block and will* call this function again after dealing with any other issues.*/
static void
maybe_start_bgworkers(void)
{
#define MAX_BGWORKERS_TO_LAUNCH 100int			num_launched = 0;TimestampTz now = 0;slist_mutable_iter iter;/** During crash recovery, we have no need to be called until the state* transition out of recovery.*/if (FatalError){StartWorkerNeeded = false;HaveCrashedWorker = false;return;}/* Don't need to be called again unless we find a reason for it below */StartWorkerNeeded = false;HaveCrashedWorker = false;/* 这里对BackgroundWorkerList进行遍历,看是否有bgworker需要start */slist_foreach_modify(iter, &BackgroundWorkerList){RegisteredBgWorker *rw;rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);/* 跳过pid非0的进程,这些bgworker已启动。*//* ignore if already running */if (rw->rw_pid != 0)continue;/* 省略部分代码行 *//* 这里拉起这些pid为0的bgworker */if (!do_start_bgworker(rw)){StartWorkerNeeded = true;return;}/* 省略部分代码行 */
}

按照这里的逻辑在PgCronLauncherMain中当接收到SIGTERM使用proc_exit(1)退出那就可以再次拉起了。

方案

给插件加入一个参数,打开参数后,当pg_terminate_backend() pg_cron launcher后就会自动被拉起

DefineCustomBoolVariable("cron.enable_autostart_launcher",gettext_noop("Allow postmaster to pull pg_cron launcher process when it is terminated by SIGTERM."),NULL,&EnableAutostartLauncher,false,PGC_POSTMASTER,GUC_SUPERUSER_ONLY,NULL, NULL, NULL);

对于进程退出的处理,打开参数时exit(1)

void
PgCronLauncherMain(Datum arg)
{MemoryContext CronLoopContext = NULL;struct rlimit limit;/* Establish signal handlers before unblocking signals. */pqsignal(SIGHUP, pg_cron_sighup);pqsignal(SIGINT, SIG_IGN);pqsignal(SIGTERM, pg_cron_sigterm);/* We're now ready to receive signals */BackgroundWorkerUnblockSignals();/* 省略部分代码行 *//* 当未接收到SIGTERM时一直在while循环中 */while (!got_sigterm){/* 省略部分代码行 */	}ereport(LOG, (errmsg("pg_cron scheduler shutting down")));/* Modify by Nickyong at 2023-02-13 PM *//* 如果cron.enable_autostart_launcher ='on' ,则proc_exit(1),否则 proc_exit(0) */if (EnableAutostartLauncher)proc_exit(1);elseproc_exit(0);/* End at 2023-02-13 PM */
}

验证

参数展示

testdb=# select * from pg_settings where name='cron.enable_autostart_launcher';
-[ RECORD 1 ]---+------------------------------------------------------------------------------------
name            | cron.enable_autostart_launcher
setting         | off
unit            | 
category        | Customized Options
short_desc      | Allow postmaster to pull pg_cron launcher process when it is terminated by SIGTERM.
extra_desc      | 
context         | postmaster
vartype         | bool
source          | configuration file
min_val         | 
max_val         | 
enumvals        | 
boot_val        | off
reset_val       | off
sourcefile      | /data/pg14-2debug/master/postgresql.auto.conf
sourceline      | 3
pending_restart | f

默认关闭

testdb=# show cron.enable_autostart_launcher;
-[ RECORD 1 ]------------------+----
cron.enable_autostart_launcher | off

terminate pg_cron launcher后没自动拉起

testdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
-[ RECORD 1 ]----+-------------------------------------------------------------------------------------------------
datid            | 24589
datname          | testdb
pid              | 23893
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | pg_cron scheduler
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2023-02-13 19:22:16.062689+08
xact_start       | 
query_start      | 2023-02-13 19:23:10.023643+08
state_change     | 2023-02-13 19:23:10.025066+08
wait_event_type  | Extension
wait_event       | Extension
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | update cron.job_run_details set status = $1, return_message = $2, end_time = $3 where runid = $4
backend_type     | pg_cron launchertestdb=# select pg_terminate_backend(23893);
-[ RECORD 1 ]--------+--
pg_terminate_backend | ttestdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
(0 rows)testdb=#

打开参数

testdb=# show cron.enable_autostart_launcher;
-[ RECORD 1 ]------------------+---
cron.enable_autostart_launcher | on

terminate pg_cron launcher后自动拉起

testdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
-[ RECORD 1 ]----+-------------------------------------------------------------------------------------------------
datid            | 24589
datname          | testdb
pid              | 24125
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | pg_cron scheduler
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2023-02-13 19:23:59.601739+08
xact_start       | 
query_start      | 2023-02-13 19:24:10.019018+08
state_change     | 2023-02-13 19:24:10.020397+08
wait_event_type  | Extension
wait_event       | Extension
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | update cron.job_run_details set status = $1, return_message = $2, end_time = $3 where runid = $4
backend_type     | pg_cron launchertestdb=# select pg_terminate_backend(24125);
-[ RECORD 1 ]--------+--
pg_terminate_backend | ttestdb=# select * from pg_stat_activity where backend_type like '%pg_cron launcher%' ;
-[ RECORD 1 ]----+------------------------------------------------------------------------------------------------------------------------------
datid            | 24589
datname          | testdb
pid              | 24329
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | pg_cron scheduler
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2023-02-13 19:24:54.976542+08
xact_start       | 
query_start      | 2023-02-13 19:24:54.978153+08
state_change     | 2023-02-13 19:24:54.981451+08
wait_event_type  | Extension
wait_event       | Extension
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | update cron.job_run_details set status = 'failed', return_message = 'server restarted' where status in ('starting','running')
backend_type     | pg_cron launchertestdb=#

小结

pg_cron的作者并没有说明这样设计的原因,我猜测是预留了一个可以强制停止所有job的入口。
如果job对于业务来说比较重要,希望被终止后可以自动拉起,以免job不调度造成一些损失,个人感觉可以做成参数来控制的方式。默认关闭,打开参数当pg_terminate_backend() 后可以自动拉起,虽然重启实例也能再次拉起pg_cron launcher,但并不是任何时候都可以重启实例的。

相关文章:

pg_cron优化案例--terminate pg_cron launcher可自动拉起

场景 在PostgreSQL中我们可以使用pg_cron来实现数据库定时任务 我有一个select 1的定时任务,每分钟触发一次 testdb# select * from cron.job ;jobid | schedule | command | nodename | nodeport | database | username | active | jobname -------…...

Python 之 NumPy 随机函数和常用函数

文章目录一、随机函数1. numpy.random.rand(d0,d1,…,dn)2. numpy.random.randn(d0,d1,…,dn)3. numpy.random.normal()4. numpy.random.randint()5. numpy.random.sample6. 随机种子np.random.seed()7. 正态分布 numpy.random.normal二、数组的其他函数1. numpy.resize()2. nu…...

【目标检测】K-means和K-means++计算anchors结果比较(附完整代码,全网最详细的手把手教程)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大努力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 一、介绍 YOLO系列目标检测算法中基于anchor的模型还是比较多的,例如YOLOv3、YOLOv4、YOLOv5等,我们可以随机初始化a…...

Java高手速成 | 图说重定向与转发

我们先回顾一下Servlet的工作原理,Servlet的工作原理跟小猪同学食堂就餐的过程很类似。小猪同学点了烤鸡腿(要奥尔良风味的),食堂窗口的服务员记下了菜单,想了想后厨的所有厨师,然后将菜单和餐盘交给专门制…...

Git:不小心在主分支master上进行修改,怎么才能将修改的数据保存到正确的分支中

1.如果还没有push commit 代码第一步:将所修改的代码提交到暂存区git stash第二步:切换到正确的分支git checkout 分支名第三步:从暂存区中取出保存到正确的分支中git stash pop第四步:重新提交git push origin 分支名2.如果已经p…...

都2023年了,如果不会Stream流、函数式编程?你确定能看懂公司代码?

👳我亲爱的各位大佬们好😘😘😘 ♨️本篇文章记录的为 Stream流、函数式编程 相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。 ♨️如果…...

亚马逊云科技汽车行业解决方案

当今,随着万物智联、云计算等领域的高速发展,创新智能网联汽车和车路协同技术正在成为车企加速发展的关键途径,推动着汽车产品从出行代步工具向着“超级智能移动终端”快速转变。 挑战无处不在,如何抢先预判? 随着近…...

为什么学了模数电还是看不懂较复杂的电路图

看懂电路并不难。 (1) 首先要摆正心态,不要看到错综复杂的电路图就一脸懵逼,不知所错。你要明白,再复杂的电路也是由一个个的基本电路拼装出来的。 (2) 基础知识当然是少不了的,常用的基本电路结构搞搞清楚。 (3) 分析电路之前先要…...

帮公司面试了一个30岁培训班出来的程序员,没啥工作经验...

首先,我说一句:培训出来的,优秀学员大有人在,我不希望因为带着培训的标签而无法达到用人单位和候选人的双向匹配,是非常遗憾的事情。 最近,在网上看到这样一个留言,引发了程序员这个圈子不少的…...

勒索软件、网络钓鱼、零信任和网络安全的新常态

当疫情来袭时,网络罪犯看到了他们的机会。随着公司办公、政府机构、学校和大学从以往的工作模式转向远程线上办公模式,甚至许多医疗保健设施都转向线上,这种快速的过渡性质导致了不可避免的网络安全漏洞。消费者宽带和个人设备破坏了企业安全…...

python3 字符串拼接与抽取

我们经常会有对字符串进行拼接和抽取的需求,下面有几个例子可以作为参考。 需求1:取出ip地址的网络地址与网络掩码进行拼接,分别使用shell脚本和python3实现 # echo "192.168.0.1"|awk -F. {print $1"."$2"."…...

Linux就该这么学:存储结构与管理硬盘

Linux系统中常见的目录名称以及相应内容 目录名称应放置文件的内容/boot开机所需文件—内核、开机菜单以及所需配置文件等/dev以文件形式存放任何设备与接口/etc配置文件/home用户主目录/bin存放单用户模式下还可以操作的命令/lib开机时用到的函数库,以及/bin与/sbin下面的命令…...

JSP四大作用域,九大内置对象

面试题:JSP和Servlet的区别?JSP的本质就是servleJSP更加侧重于视图的展示,servlet更注重逻辑的处理。面试题:include指令和jsp:include标签的区别?从效果上来说,没区别。include指令是把两个页面合成一个js…...

机器学习笔记之生成模型综述(五)重参数化技巧(随机反向传播)

机器学习笔记之生成模型综述——重参数化技巧[随机反向传播]引言回顾神经网络的执行过程变分推断——重参数化技巧重参数化技巧(随机反向传播)介绍示例描述——联合概率分布示例描述——条件概率分布总结引言 本节将系统介绍重参数化技巧。 回顾 神经网络的执行过程 上一节…...

1、创建第一个Android项目

1.1、创建Android工程项目:双击打开Android Studio。在菜单栏File中new-->new project3、在界面中选择Empty Activity,然后选择next4、在下面界面中修改工程名称,工程保存路径选择java语言,然后点击finishAndroid studio自动为…...

【python百炼成魔】手把手带你学会python数据类型

文章目录前言一. python的基本数据类型1.1 如何查看数据类型1.2 数值数据类型1.2.1 整数类型1.2.2 浮点数类型1.2.3 bool 布尔数值类型1.2.4 字符串类型二. 数据类型强制转换2.1 强制转换为字符串类型2.2 强制转换为int类型2.3 强制转换函数之float() 函数三. 拓展几个运算函数…...

数据储存以及大小端判断

目录 数据存储 1,二进制存储方式(补码,反码,源码) 2,指针类型 3,大端,小段判断 1,二进制存储方式(补码,反码,源码) 我…...

GRASP设计原则

GRASP设计原则介绍9种基本原则创建者 Creator问题解决方法何时不使用?好处信息专家 Information Expert问题解决方法信息怎么做优点低耦合 Low Coupling耦合问题解决方法原则何时不使用?控制器 Controller问题解决方法外观控制器会话控制器优点臃肿控制器的解决方法高内聚 Hi…...

再遇周杰伦隐私协议

本隐私信息保护政策版本:2021 V1 一、重要提示 请您(以下亦称“用户”)在使用本平台App时仔细阅读本协议之全部条款,并确认您已完全理解本协议之规定,尤其是涉及您的重大权益及义务的加粗或划线条款。如您对协议有任…...

关于项目上的一些小操作记录

一 如何在项目的readme.md文件中插入图片说明 1 准备一张图片命名为test.png 2 在maven项目的resources目录下新建文件夹picture,将图片放入该目录下 3 在readme.md文件中期望插入图片的地方编辑如下: ![Alt](src/main/resources/picture/test.png) 此时&#…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

python打卡day49

知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

【Oracle APEX开发小技巧12】

有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

条件运算符

C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...

生成 Git SSH 证书

🔑 1. ​​生成 SSH 密钥对​​ 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​: -t rsa&#x…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...