apscheduler 定时任务框架

news/2024/4/25 21:01:53/文章来源:https://blog.csdn.net/hj1993/article/details/129251396

Apscheduler 介绍

四大组件

  • triggers:触发器,用于设定触发任务的条件
  • job stores:作业存储器,用于存放任务,可以存放在数据库或内存,默认内存
  • executors:执行器,用于执行任务,可以设定执行默认为单线程或线程池
  • schedulers:调度器,将上述三个组件作为参数,通过创建调度器实例来执行

触发器 triggers

每个任务都有自己的触发器,它可以决定任务触发的条件,触发器默认是无状态的。

作业存储器 job stores

默认存储在内存中,若存储到数据库中会有个序列化和反序列化的过程,同时修改和搜索任务的功能也是由它实现。

一个作业存储器不要共享给多个调度器,不然会造成状态混乱

执行器 executors

将任务放入线程或线程池中执行,执行完毕通知调度器

调度器 schedulers

调度器提供接口,可以将触发器、作业存储器和执行器整合起来,从而实现对任务的操作。

调度器组件

  • BlockingScheduler 阻塞式调度器:适用于只跑调度器的程序。
  • BackgroundScheduler 后台调度器:适用于非阻塞的情况,调度器会在后台独立运行。
  • AsyncIOScheduler AsyncIO调度器,适用于应用使用AsnycIO的情况。
  • GeventScheduler Gevent调度器,适用于应用通过Gevent的情况。
  • TornadoScheduler Tornado调度器,适用于构建Tornado应用。
  • TwistedScheduler Twisted调度器,适用于构建Twisted应用。
  • QtScheduler Qt调度器,适用于构建Qt应用。

选择正确的调度器、作业存储器、触发器和执行器

1、作业存储器

  • 作业不需要持久化:默认的 MemoryJobStore
  • 作业需要持久化:作业在调度程序重启或应用程序奔溃后继续存在,推荐采用:SQLAlchemyJobStore + PostgreSQL

2、执行器

  • 默认 ThreadPoolExecutor 线程池足以满足大多数场景
  • CPU 密集型操作:应考虑 ProcessPoolExecutor 进程池,来充分利用多核算力。也可以将 ProcessPoolExecutor 作为第二执行器,混合使用两种不同的执行器。

触发器详解

一个任务可以设定多种触发器,如全部条件满足触发、满足其一触发以及复合触发等:

可参考:https://apscheduler.readthedocs.io/en/latest/modules/triggers/combining.html#module-apscheduler.triggers.combining

内置的三种触发器类型

  • date:在特定时间仅允许一次作业
  • interval:固定时间间隔允许作业
  • cron:一天中特定时间定期允许作业

指定时间任务 date

三种类型:date/datetime/字符串,不加时间则立即执行:

from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job(text):print(text)sched.add_job(my_job, 'date', run_date=date(2009, 11, 6), args=['text'])
sched.add_job(my_job, 'date', run_date=datetime(2020, 1, 7, 14, 35, 2), args=['text'])
sched.add_job(my_job, 'date', run_date='2009-11-06 16:30:05', args=['text'])
sched.add_job(my_job, args=['text'])sched.start()

参考:apscheduler.triggers.date

间隔任务 interval

from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
import osdef tick():print('当前时间:', datetime.now())if __name__ == '__main__':scheduler = BlockingScheduler()  # 默认调度器,存入在内存中scheduler.add_job(tick, 'interval', seconds=3)  # 添加到作业中print('按 Ctrl+{0} 终端任务'.format('Break' if os.name == 'nt' else 'c  '))try:scheduler.start()except (KeyboardInterrupt, SystemError):pass

运行结果如下:

按 Ctrl+Break 终端任务
当前时间: 2020-01-07 13:55:37.540614
当前时间: 2020-01-07 13:55:40.540879
当前时间: 2020-01-07 13:55:43.542759
当前时间: 2020-01-07 13:55:46.542512
当前时间: 2020-01-07 13:55:49.541907
当前时间: 2020-01-07 13:55:52.541845
当前时间: 2020-01-07 13:55:55.542011
当前时间: 2020-01-07 13:55:58.542533

指定开始、结束时间:

# 指定开始、结束时间
from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())sched.add_job(my_job, 'interval', seconds=3, start_date='2020-01-07 14:45:20', end_date='2020-01-07 14:46:20')sched.start()

装饰器:

@sched.scheduled_job('interval', id='job_id', seconds=3)
def my_job():print('当前时间:', datetime.now())

jitter 振动参数,给每次触发添加一个随机浮动秒数,一般适用于多服务器,避免同时运行造成服务拥堵。

# 每小时(上下浮动120秒区间内)运行`job_function`
sched.add_job(job_function, 'interval', hours=1, jitter=120)

参考:apscheduler.triggers.interval

crontab表达式 cron

参数

pscheduler.triggers.cron.CronTrigger(year = None,month = None,day = None,week = None,day_of_week = None,hour = None,minutes = None,second = None,start_date = None,end_date = None,timezone = None,jitter = None

参数详解

  • year (int|str) – 4-digit year
  • month (int|str) – month (1-12)
  • day (int|str) – day of the (1-31)
  • week (int|str) – ISO week (1-53)
  • day_of_week (int|str) – number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun)
  • hour (int|str) – hour (0-23)
  • minute (int|str) – minute (0-59)
  • second (int|str) – second (0-59)
  • start_date (datetime|str) :开始触发事件
  • end_date (datetime|str) :结束时间
  • timezone (datetime.tzinfo|str) 用于日期、时间计算的时区,默认为调度程序时区
  • jitter (int|None) :作业最多延迟多久执行

表达式类型

表达式参数类型描述
  • | 所有 | 通配符,minute=* 即每分钟触发一次
    */a | 所有 | 可被 a 整除的通配符
    a-b | 所有 | 范围 a -b 触发
    a-b/c | 所有 | 范围 a-b,且可被 c 整除时触发
    xth y | 日 | 第几个星期几触发,x 为第几个,y 为星期几
    last x | 日 | 一个月中,最后那个星期几触发
    last | 日 | 一个月中最后一天触发
    x,y,z | 所有 | 组合表达式,可组合确定值或上方表达式

示例一

from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())# 6/7/8 和 11/12 月的第三个周五的 0/1/2/3 点触发
sched.add_job(my_job, 'cron', month='6-8, 11-12', day='3rd fri', hour='0-3')sched.start()

示例二:指定时间范围

# 周一到周日,每天 15::9 触发,截止时间:2020-01-08
sched.add_job(my_job, 'cron', day_of_week='0-6', hour=15, minute=29, end_date='2020-01-08')

示例三:装饰器

# 每个月的最后一个星期日触发
@sched.scheduled_job('cron', id='job_id', day='last 6')
def my_job():print('当前时间:', datetime.now())

示例四:标准 crontab 表达式

sched.add_job(my_job, CronTrigger.from_crontab('0 0 1-15 may-aug *'))

添加 jitter 随机执行,适用于多台服务器在不同时间执行:

# 每小时上下浮动120秒触发
sched.add_job(my_job, 'cron', hour='*', jitter=120)

夏令时问题

有些时区可能因为夏令时问题,导致时区切换时,任务不执行或执行两次,这不是错误,要避免这个问题,可使用 UTC 时间,或提前规避,以下写法可能会导致错误:

# 在Europe/Helsinki时区, 在三月最后一个周一就不会触发;在十月最后一个周一会触发两次
sched.add_job(job_function, 'cron', hour=3, minute=30)

参考:apscheduler.triggers.cron

配置调度器

可通过直接传字典、传参或实例一个调度器对象,再添加配置信息的形式来配置调度器。

创建一个默认作业存储器和执行器:

from apscheduler.schedulers.background import BackgroundSchedulerscheduler = BackgroundScheduler()
  • 调度器:defaultMemoryJonStore (内存任务存储器)
  • 执行器:default ,最大线程数为 10 的 ThreadPoolExecutor 线程池执行器

示例

应用场景:

两个作业存储器搭配两个执行器,同时又要修改作业的默认参数,还有修改时区。

  • 名为 mongo 的 MemoryDBJobStore
  • 名为 default 的 SQLAlchemyJobStore
  • 名为 TreadPoolExecutor 的 ThreadPoolExecutor,最大线程 20
  • 名为 processpool 的 ProcessPoolExecutor,最大进程 5
  • UTC 时区作为默认调度器时区
  • 默认为新任务关闭合并模式
  • 设置新任务的默认最大实例数为 3

1、方法一:

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor# 作业存储器
jobstores = {'mongo': MongoDBJobStore(),'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}# 执行器
executors = {'default': ThreadPoolExecutor(20),'processpool': ProcessPoolExecutor(5)
}job_defaults = {'coalesce': False,      # 关闭作业合并'max_instances': 3
}scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)

2、方法二:

from apscheduler.schedulers.background import BackgroundScheduler# The "apscheduler." prefix is hard coded
scheduler = BackgroundScheduler({'apscheduler.jobstores.mongo': {'type': 'mongodb'},'apscheduler.jobstores.default': {'type': 'sqlalchemy','url': 'sqlite:///jobs.sqlite'},'apscheduler.executors.default': {'class': 'apscheduler.executors.pool:ThreadPoolExecutor','max_workers': '20'},'apscheduler.executors.processpool': {'type': 'processpool','max_workers': '5'},'apscheduler.job_defaults.coalesce': 'false','apscheduler.job_defaults.max_instances': '3','apscheduler.timezone': 'UTC',
})

3、方法三:

from pytz import utcfrom apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ProcessPoolExecutorjobstores = {'mongo': {'type': 'mongodb'},'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {'default': {'type': 'threadpool', 'max_workers': 20},'processpool': ProcessPoolExecutor(max_workers=5)
}
job_defaults = {'coalesce': False,'max_instances': 3
}
scheduler = BackgroundScheduler()# .. do something else here, maybe add jobs etc.scheduler.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)

启用调度

调用 start() 即可启用调度,对于非阻塞的会立即返回,对于 BlockingScheduler 会阻塞的 start 位置,因此运行其他代码要写在 start 之前。

调度器启动后,就不能修改配置了。

添加任务

  • add_job():返回一个实例对象,通过对象可以修改、删除任务
  • 装饰器:scheduled_job():运行时不能修改任务

任何时候都可添加任务,若调度器未启动,那么任务会处于一个暂存状态。当调度器启动时,才会计算下次运行时间。

若执行器和作业存储器是要序列化的任务的,那么必须满足

  • 回调函数必须全局可用
  • 回调函数参数必须可用被序列化

内置任务储存器中,只有MemoryJobStore不会序列化任务;内置执行器中,只有ProcessPoolExecutor会序列化任务。

另外若程序初始化时,从数据库读取任务,则必须为每个任务定义一个 ID,并使用 replace_existing=True,否则每次重启程序,都会得到一个新的任务拷贝,即前一个任务状态不会被保存。

Tips:立即执行任务,可在添加任务时省略 trigger 参数

移除任务

从调度器移除任务,也必须移除作业存储器中的任务。

  • remove_job(任务 ID):参数为任务 ID 或作业存储器名称
  • 调用 job=add_job()、job.remove() 移除

对于通过 scheduled_job() 创建的任务,只能选择第一种方式。

示例:

job = sched.add_job(func, 'interval', minutes=2)
job.remove()sched.add_job(func, 'interval', minute=2, id='job_id')
sched.remove_job('job_id')

暂停恢复任务

from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())job = sched.add_job(my_job, 'cron', id='job_id', month='6-8, 11-12', day='3rd fri', hour='0-3')# 暂停作业
job.pause()
sched.pause_job('job_id')# 恢复作业
job.resume()
sched.remove_job('job_id')sched.start()

获取任务列表

from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())job = sched.add_job(my_job, 'cron', id='job_id', month='6-8, 11-12', day='3rd fri', hour='0-3')print('当前任务', sched.get_job('job_id'))
print('任务列表', sched.get_jobs())
print('格式化作业列表', sched.print_jobs())sched.start()

运行结果如下:

当前任务 my_job (trigger: cron[month='6-8,11-12', day='3rd fri', hour='0-3'], pending)
任务列表 [<Job (id=job_id name=my_job)>]
Pending jobs:my_job (trigger: cron[month='6-8,11-12', day='3rd fri', hour='0-3'], pending)
格式化作业列表 None

print_jobs() 可以快速打印格式化的任务列表,包含触发器,下次运行时间等信息。

修改任务

# 可修改除 ID 以外其他任务属性
job.modify(max_instances=6, name='Alternate name')
sched.modify_job(max_instances=6, name='Alternate name')# 修改触发器
# job.reschedule('job_id', trigger='cron', minute='*/5')
sched.reschedule_job('job_id', trigger='interval', minutes=4)

示例:

from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())job = sched.add_job(my_job, 'interval', id='job_id', minutes=3)print('当前任务', sched.get_job('job_id'))# job.reschedule('job_id', trigger='cron', minute='*/5')
sched.reschedule_job('job_id', trigger='interval', minutes=4)
print('修改后的任务:', sched.get_job('job_id'))sched.start()

运行结果如下:

当前任务 my_job (trigger: interval[0:03:00], pending)
修改后的任务: my_job (trigger: interval[0:04:00], next run at: 2020-01-07 16:44:45 CST)

关闭调度

sched.shutdown()
sched.shutdown(wait=False)  # 不等待正在运行的任务

限制作业并发执行实例数量

默认情况下,在同一时间,一个任务只允许一个执行中的实例在运行。比如说,一个任务是每5秒执行一次,但是这个任务在第一次执行的时候花了6秒,也就是说前一次任务还没执行完,后一次任务又触发了,由于默认一次只允许一个实例执行,所以第二次就丢失了。为了杜绝这种情况,可以在添加任务时,设置 max_instances 参数,为指定任务设置最大实例并行数。

丢失任务的执行与合并
有时,任务会由于一些问题没有被执行。最常见的情况就是,在数据库里的任务到了该执行的时间,但调度器被关闭了,那么这个任务就成了“哑弹任务”。错过执行时间后,调度器才打开了。这时,调度器会检查每个任务的 misfire_grace_time 参数 int 值,即哑弹上限,来确定是否还执行哑弹任务(这个参数可以全局设定的或者是为每个任务单独设定)。此时,一个哑弹任务,就可能会被连续执行多次。

但这就可能导致一个问题,有些哑弹任务实际上并不需要被执行多次。 coalescing 合并参数就能把一个多次的哑弹任务揉成一个一次的哑弹任务。也就是说,coalescingTrue 能把多个排队执行的同一个哑弹任务,变成一个,而不会触发哑弹事件。

注!如果是由于线程池/进程池满了导致的任务延迟,执行器就会跳过执行。要避免这个问题,可以添加进程或线程数来实现或把 misfire_grace_time 值调高。

调度事件监听

任务执行时有可能会出现错误,那么如何第一时间指定在哪发生错误呢? apscheduler
给我们提供了事件监听来解决这个问题。

from datetime import date, datetimefrom apscheduler.schedulers.blocking import BlockingSchedulersched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())print(1/0)

上述代码每 5 秒钟执行一次,每次都会发生错误。我们给其添加一个回调函数和日志记录来监听:

from datetime import date, datetime
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
import logginglogging.basicConfig(level=logging.INFO,format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S',filename='log.txt',filemode='a')sched = BlockingScheduler()def my_job():print('当前时间:', datetime.now())print(1 / 0)def test_job():print('正常任务!', datetime.now())def my_listener(event):if event.exception:print('任务运行出错!', datetime.now())else:print('任务正常运行!', datetime.now())job = sched.add_job(my_job, 'cron', second='*/5')
job1 = sched.add_job(test_job, 'interval', seconds=3)
sched.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
sched._logger = loggingsched.start()

事件类型

ConstantDescriptionEvent class
EVENT_SCHEDULER_STARTEDThe scheduler was started SchedulerEvent
EVENT_SCHEDULER_SHUTDOWNThe scheduler was shut down SchedulerEvent
EVENT_SCHEDULER_PAUSEDJob processing in the scheduler was paused SchedulerEvent
EVENT_SCHEDULER_RESUMEDJob processing in the scheduler was resumed SchedulerEvent
EVENT_EXECUTOR_ADDEDAn executor was added to the scheduler SchedulerEvent
EVENT_EXECUTOR_REMOVEDAn executor was removed to the scheduler SchedulerEvent
EVENT_JOBSTORE_ADDEDA job store was added to the scheduler SchedulerEvent
EVENT_JOBSTORE_REMOVEDA job store was removed from the scheduler SchedulerEvent
EVENT_ALL_JOBS_REMOVEDAll jobs were removed from either all job stores or one particular job store SchedulerEvent
EVENT_JOB_ADDEDA job was added to a job store JobEvent
EVENT_JOB_REMOVEDA job was removed from a job store JobEvent
EVENT_JOB_MODIFIEDA job was modified from outside the scheduler JobEvent
EVENT_JOB_SUBMITTEDA job was submitted to its executor to be run JobSubmissionEvent
EVENT_JOB_MAX_INSTANCESA job being submitted to its executor was not accepted by the executor because the job has already reached its maximum concurrently executing instances JobSubmissionEvent
EVENT_JOB_EXECUTEDA job was executed successfully JobExecutionEvent
EVENT_JOB_ERRORA job raised an exception during execution JobExecutionEvent
EVENT_JOB_MISSEDA job’s execution was missed JobExecutionEvent
EVENT_ALLA catch-all mask that includes every event type N/A

日志

import logginglogging.basicConfig()
logging.getLogger('apscheduler').setLevel(logging.DEBUG)

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

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

相关文章

springboot+pgbouncer+postgres数据库连接池集成方案及问题解决

期望通过每一次分享&#xff0c;让技术的门槛变低&#xff0c;落地更容易。 —— around 前言 旨在解决微服务项目全是连接池并影响数据库并发连接&#xff0c;作者的环境是基于sprongboot微服务连接postgres数据库&#xff0c;每个微服务的DAO层配置都使用了连接池技术。后续…

PGLBox 超大规模 GPU 端对端图学习训练框架正式发布

作者 | PGLBox项目组 导读 PGLBox是百度研发的基于GPU的大规模图模型训练框架&#xff0c;支持数百亿节点和边的图模型全GPU训练&#xff0c;已在百度广泛部署。相比业界主流的分布式 CPU 解决方案&#xff0c;PGLBox 具有超高性能、超大规模、算法丰富、灵活易用、落地广泛等优…

复习知识点十之方法的重载

目录 方法的重载 练习1: 练习1: 数组遍历 练习2: 数组的最大值 练习3: 练习4: 复制数组 基本数据类型和引用数据类型 方法的重载 Java虚拟机会通过参数的不同来区分同名的方法 练习1: public class Test4 {public static void main(String[] args) {//调用方法 // …

【架构师】零基础到精通——服务与网关

博客昵称&#xff1a;架构师Cool 最喜欢的座右铭&#xff1a;一以贯之的努力&#xff0c;不得懈怠的人生。 作者简介&#xff1a;一名Coder&#xff0c;软件设计师/鸿蒙高级工程师认证&#xff0c;在备战高级架构师/系统分析师&#xff0c;欢迎关注小弟&#xff01; 博主小留言…

Raki的读paper小记:BART

Abstract&Introduction&Related Work 研究任务 预训练语言模型已有方法和相关工作 BERT等面临挑战 已有的方法依赖下游任务创新思路 融合了双向和自回归模型随机打乱原始句子的顺序&#xff0c;并同时使用一种novel的填空方案&#xff0c;文本的span只用一个masked to…

近期常见组件漏洞更新:

&#xff08;1&#xff09;mysql 5.7 在2023年1月17日&#xff0c;发布了到5.7.41版本 mysql 8.0 在2023年1月17日&#xff0c;发布了到8.0.32版本 MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ &#xff08;2&#xff09;Tomcat8在202…

RedisTemplate 的基本使用手把手教

下载实例源码 使用步骤 1、引入 spring-boot-starter-data-redis 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>2、在 application.yml 配置 R…

springboot使用Gateway搭建网关服务及Nacos实现动态路由

实际工作中我们会有很多个项目&#xff0c;这些项目共同使用同一个网关gateway来实现路由&#xff0c;各个项目之间调用以及前端调用都可以直接通过服务名称来调用&#xff0c;不用管ip&#xff0c;后续项目迁移到其它服务器也不受影响。 首先搭建springboot微服务&#xff0c…

CANoe TC8测试脚本的结构介绍

CANoe TC8脚本是通过vTESTstudio平台编写。每个协议(ARP\ICMPv4\IPv4\UDP\TCP\SOMEIP\DHCP)都有自己的vtt文件。每个vtt文件的测试树结构为: Test Fixture Fixture Preparation Test Case Test Case … Test Case Test Case Fixture Completion 当Test Fixture里的Test Case…

分布式ID生成系统

目录背景常用分布式ID生成系统介绍UUIDSnowflake背景 在大多数复杂的分布式系统中&#xff0c;往往需要对大量的数据和消息进行唯一标识。而对分布式系统后台数据库的分库分表后需要有一个唯一的ID来表示一条数据或者是消息。那么我们分布式系统ID一般都有哪些需求呢&#xff1…

初识CSS

1.CSS语法形式CSS基本语法规则就是:选择器若干属性声明由选择器选择一个元素,其中的属性声明就作用于该元素.比如:<body><p>这是一个段落</p><!-- style可以放在代码的任意地方 --><style>p{/* 将字体颜色设置为红色 */color: red;}</style&g…

C++修炼之练气期一层——命名空间

目录 1.引例 2.命名空间的定义 3.命名空间的使用 4.命名空间使用注意事项 1.引例 #include <stdio.h> #include <stdlib.h>int rand 10;int main() {printf("%d\n", rand);return 0; } 当我们用C语言写下这样的代码&#xff0c;看着并没有什么语法…

【手把手一起学习】(七) Altium Designer 20常用PCB设计规则

1 常用PCB设计规则 PCB规则设计是PCB设计中至关重要的环节&#xff0c;它约束了电气要求、布线方式、器件摆放位置等&#xff0c;为后续的手动布局、布线提供依据。完善的PCB规则设计&#xff0c;可以减少设计中的错误&#xff0c;提高PCB设计效率。 1.1 PCB设计规则管理器 …

谷歌搜索技巧大全 | 谷歌高级搜索语法指令

谷歌搜索技巧是利用各种高级搜索语法或者搜索指令&#xff0c;让我们能够使用Google进行精确化的搜索&#xff0c;外贸找客户和学术文件查找都可以应用到这些搜索技巧。(大部分命令也适用百度搜索)。Google通过互联网收集数据&#xff0c;抓取有意义的信息&#xff0c;将其存储…

【知识图谱】架构-特点-缺点简介

架构物联网、云计算、人工智能等新一代信息技术的迅猛发展&#xff0c;带来了制造业的新一轮突破&#xff0c;推动着制造系统向智能化方向发展&#xff0c;驱动着未来制造模式的创新。其中数据和知识是实现制造业与新一代信息技术融合的基础&#xff0c;是实现智能制造的保障。…

C++杂谈(一)

前言 本系列也是慢更系列&#xff0c;主要收纳一些还不够单独成系列的C的杂项问题&#xff0c;或是一些与C有关&#xff0c;但不属于核心知识的一些旁系问题。 关于C与C的关系 「学C要先学C吗&#xff1f;」 「C和C是不是完全不同的两个语言&#xff1f;」 「这个语法是C的还…

Android 动画详解

Android动画的分类与使用学习Android必不可少的就是动画的使用了&#xff0c;在Android版本迭代的过程中&#xff0c;出现了很多动画框架&#xff0c;这里做一个总结。Android动画类型分类逐帧动画【Frame Animation】&#xff0c;即顺序播放事先准备的图片。补间动画【Tween A…

NSGA-Ⅲ源代码

NSGA-Ⅲ源代码如下&#xff0c;供大家学习和应用。该算法在梯级水电-火电的应用订阅专栏即可查看&#xff1a; 1、主函数 % % Copyright (c) 2016, Mostapha Kalami Heris & Yarpiz (www.yarpiz.com) % All rights reserved. Please read the "LICENSE" file…

前端经典react面试题及答案

为什么 React 元素有一个 $$typeof 属性 目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化&#xff0c;所以 React 可以通过有没有 $$typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。 如果没有 $$typeof 这个属性&#xff0c;react 会拒绝处理该元素。…

C++plog库,轻量级日志框架(日志库)

文章目录主要头文件及使用方法解释plog/Log.hplog/Appenders/ColorConsoleAppender.hplog/Appenders/RollingFileAppender.hplog/Formatters/TxtFormatter.h三个核心概念Formatter&#xff1a;格式化程序格式化程序举例TxtFormatterJsonFormatterCsvFormatterSyslogFormatterAp…