Elixir GenServer
GenServer
是一个用来实现客户端-服务器模式中服务器的行为模块。
GenServer
是一个普通的 Elixir 进程,同其他 Elixir 进程一样,它可以用来保存状态、异步执行代码等。使用这个模块来实现通用服务器进程(GenServer)的优势在于,它有一套标准的接口函数,并提供用于跟踪和错误报告的功能。同时它也可以用到一个监督树中。
示例
GenServer
行为抽象了常见的客户端-服务器模式。开发人员只需要实现他们感兴趣的回调和功能。
让我们从一个例子开始,然后探索可用的回调。假设我们想用 GenServer 实现一个栈服务,可以压入和弹出元素。我们将通过实现三个回调来定制一个通用 GenServer。
init/1
将我们的初始参数转换为 GenServer 的初始状态。 handle_call/3
在服务器收到同步的 pop
消息时触发,从栈中弹出一个元素并返回给用户。 handle_cast/2
在服务器收到异步的 push
消息时触发,将一个元素压入栈中:
defmodule Stack douse GenServer# Callbacks@impl truedef init(elements) doinitial_state = String.split(elements, ",", trim: true){:ok, initial_state}end@impl truedef handle_call(:pop, _from, state) do[to_caller | new_state] = state{:reply, to_caller, new_state}end@impl truedef handle_cast({:push, element}, state) donew_state = [element | state]{:noreply, new_state}end
end
我们将进程的启动、消息传递和消息循环机制留给了 GenServer 行为,我们只用关注栈的具体实现。现在我们可以创建一个进程,并使用 GenServer API 发送消息来与服务交互了:
# Start the server
{:ok, pid} = GenServer.start_link(Stack, "hello,world")# This is the client
GenServer.call(pid, :pop)
#=> "hello"GenServer.cast(pid, {:push, "elixir"})
#=> :okGenServer.call(pid, :pop)
#=> "elixir"
我们通过调用 start_link/2
来启动我们的 Stack
,它需要服务的实现模块和初始参数。GenServer 行为会调用 init/1
回调来生成 GenServer 的初始状态。从此刻开始,GenServer 接管一切,因此我们可以通过向它发送两种类型的消息与它交互。call 消息期望从服务器获得回复(因此是同步的),而 cast 消息则不需要回复。
GenServer.call/3
产生的消息必须由 GenServer 的 handle_call/3
回调处理。而 cast/2
的消息必须由 handle_cast/2
处理。 GenServer
支持 8 个回调,但只有 init/1
是必需的。
当你使用
use GenServer
时,GenServer
模块会设置@behaviour GenServer
并定义一个child_spec/1
函数,因此你的模块可以做为监督树的子进程。
客户端/服务端 API
尽管在上述示例中我们直接使用了 GenServer.start_link/3
和相关函数来启动服务器并与之通信,但大多数时候我们不直接调用 GenServer
函数。相反,我们将调用封装到新的函数中,做为服务器的公共 API。这些封装被称为客户端 API。
以下是我们栈模块的升级版:
defmodule Stack douse GenServer# Clientdef start_link(default) when is_binary(default) doGenServer.start_link(__MODULE__, default)enddef push(pid, element) doGenServer.cast(pid, {:push, element})enddef pop(pid) doGenServer.call(pid, :pop)end# Server (callbacks)@impl truedef init(elements) doinitial_state = String.split(elements, ",", trim: true){:ok, initial_state}end@impl truedef handle_call(:pop, _from, state) do[to_caller | new_state] = state{:reply, to_caller, new_state}end@impl truedef handle_cast({:push, element}, state) donew_state = [element | state]{:noreply, new_state}end
end
实际上,将服务器和客户端的函数放在同一个模块中是很常见的。如果服务器或客户端变得越来越复杂,你可能希望将它们放到不同的模块中。
下图总结了客户端和服务器之间的交互。客户端和服务器都是进程,它们通过消息(实线)进行通信。服务器与实现模块的交互发生在 GenServer 进程调用你的代码时(虚线):
如何监督
一个 GenServer
通常在监督树下启动。当我们调用 use GenServer
时,它会自动定义了一个 child_spec/1
函数,让我们可以直接在监督者下启动 Stack
。通过以下代码我们可以在监督者下启动一个初始为 ["hello", "world"]
的栈:
children = [{Stack, "hello,world"}
]Supervisor.start_link(children, strategy: :one_for_all)
use GenServer
还接受一个选项列表来配置子进程描述,控制它在监督者下的运行方式。生成的 child_spec/1
可以使用以下选项自定义:
:id
- 子进程描述的唯一标识,默认是当前模块名。:restart
- 子进程是否应该被重启,默认为:permanent
。:shutdown
- 如何关闭子进程,立即关闭或给一段时间关闭。
例如:
use GenServer, restart: :transient, shutdown: 10_000
详细信息还请参见 Supervisor
模块中的“子进程描述”部分。紧接在 use GenServer
之前的 @doc
注释会添加到生成的 child_spec/1
函数上。
当停止 GenServer 时,例如从回调返回 {:stop, reason, new_state}
元组,退出原因会被监督者用来确定是否需要重启 GenServer。更多信息请参见 Supervisor
模块中的“退出原因和重启”部分。
命名注册
start_link/3
和 start/3
都支持 GenServer
在启动时通过 :name
选项注册一个名称。注册的名称也会在进程终止时自动清理。支持的值包括:
- 一个原子 - 使用
Process.register/2
,GenServer 以此为名称注册到本地(当前节点)。 {:global, term}
- 使用:global
模块的函数将 GenServer 注册到全局,term 是服务名称。{:via, module, term}
- 使用自定义的机制和名称注册服务。:via
选项需要一个导出了register_name/2
、unregister_name/1
、whereis_name/1
和send/2
函数的模块。:global
就是一个这样的模块,它使用这些函数来管理进程名称和PID,使它们对 Elixir 节点网络全局可用。Elixir 还提供了一个本地的、去中心化的、可扩展的注册表叫做Registry
,用于本地存储动态生成的名称。
进程名称和 PID 之间的关系就像域名和 IP 地址一样。
例如,我们可以像下面这样本地启动并注册 Stack
服务:
# Start the server and register it locally with name MyStack
{:ok, _} = GenServer.start_link(Stack, "hello", name: MyStack)# Now messages can be sent directly to MyStack
GenServer.call(MyStack, :pop)
#=> "hello"
一旦服务启动, GenServer
模块的其他函数(call/3
、 cast/2
等)除了可以通过 PID 来调用,也可以通过 {:global, ...}
或 {:via, ...}
元组调用。总的来说支持以下格式:
- PID
- 原子,如果服务是本地注册的话
{atom, node}
,服务在另一个节点本地注册{:global, term}
,服务是全局注册的话{:via, module, name}
,服务通过其他模块自定义注册
如果想要在本地注册动态名称,请不要使用原子,因为原子不会被垃圾回收,因此动态生成的原子也不会被垃圾回收。对于这种情况,可以使用 Registry
模块做为自己的本地注册服务。
接收“常规”消息
GenServer
的目标是为开发者抽象“接收”循环,自动处理系统消息,支持代码变更、同步调用等。因此,永远不要在 GenServer 回调中调用你自己的“接收”代码,这样做会导致 GenServer 行为异常。
除了 call/3
和 cast/2
提供的同步和异步通信外,通过 send/2
、 Process.send_after/4
等发送的“常规”消息可以在 handle_info/2
回调中处理。
handle_info/2
可用于许多情况,例如处理由 Process.monitor/1
发送的监控 DOWN 消息。 handle_info/2
的另一个用例是借助 Process.send_after/4
执行周期性工作:
defmodule MyApp.Periodically douse GenServerdef start_link(_) doGenServer.start_link(__MODULE__, %{})end@impl truedef init(state) do# Schedule work to be performed on startschedule_work(){:ok, state}end@impl truedef handle_info(:work, state) do# Do the desired work here# ...# Reschedule once moreschedule_work(){:noreply, state}enddefp schedule_work do# We schedule the work to happen in 2 hours (written in milliseconds).# Alternatively, one might write :timer.hours(2)Process.send_after(self(), :work, 2 * 60 * 60 * 1000)end
end
超时
init/1
或任何 handle_*
回调的返回值都可以包含一个单位为毫秒的超时时间;如果没有,则为 :infinity
。超时可以用来检测没有消息的空闲期。
timeout()
的用法如下:
- 如果在超时到达时进程有正在等待处理的消息,超时将被忽略,等待的消息会像往常一样被处理。这意味着即使是 0 毫秒的超时也不能保证被执行(如果你想立即无条件地执行某个动作,请改用
:continue
指令)。 - 如果在超时到达之前有消息到达,超时将被清除,该消息会像往常一样被处理。
- 否则,当超时时间过去后没有消息到达,
handle_info/2
将被调用,并以:timeout
作为第一个参数。
何时(不)使用 GenServer
到目前为止,我们已经了解到 GenServer 可以做为被监督的进程用来处理同步和异步调用。还可以处理系统消息,如周期性消息和监控事件。GenServer 进程也可以被命名。
一个 GenServer,或者更通俗的说,一个进程,是用来建模系统的运行时特性的,绝不能被用来组织代码。
在 Elixir 中,代码通过模块和函数来组织,进程不是必须的。例如,想象你正在实现一个计算器,你决定将所有的计算操作放在一个 GenServer 后面:
def add(a, b) doGenServer.call(__MODULE__, {:add, a, b})
enddef subtract(a, b) doGenServer.call(__MODULE__, {:subtract, a, b})
enddef handle_call({:add, a, b}, _from, state) do{:reply, a + b, state}
enddef handle_call({:subtract, a, b}, _from, state) do{:reply, a - b, state}
end
这就是一个反模式,不仅是因为它使计算器逻辑变得复杂,而且因为你将计算逻辑放在了一个单一的进程里面,这个进程可能会成为你系统的瓶颈,特别是随着调用数量增加时。直接定义为函数就行了:
def add(a, b) doa + b
enddef subtract(a, b) doa - b
end
能不使用进程就不要使用进程。只有在需要建模运行时属性时才会使用进程,如可变状态、并发和失败,而不是为了组织代码。
使用 :sys
模块进行调试
GenServers 作为一种特殊进程,可以使用 :sys
模块进行调试。通过各种钩子,这个模块允许开发者查看进程的状态,并跟踪执行期间发生的系统事件,如收到的消息、发送的回复以及状态变化。
以下是 :sys
模块用于调试的基本函数:
:sys.get_state/2
- 检索进程的状态。在 GenServer 中下,就是回调模块状态,即传递给回调函数的最后一个参数。:sys.get_status/2
- 检索进程的状态。这个状态包括进程字典,如果进程正在运行或被挂起,父 PID,调试器状态,以及行为模块的状态,其中包括回调模块状态(由:sys.get_state/2
返回)。可以通过实现可选的GenServer.format_status/1
回调来改变这个状态的格式。:sys.trace/3
- 将所有系统事件打印到:stdio
。:sys.statistics/3
- 管理(打开/关闭)进程统计信息的收集。:sys.no_debug/2
- 关闭给定进程的所有调试句柄。结束后关闭调试是非常重要的。过多的调试句柄或者本应关闭但没有关闭的,可能会严重影响系统的性能。:sys.suspend/2
- 挂起一个进程,让它只回复系统消息,不回复其他消息。挂起的进程可以通过:sys.resume/2
重新激活。
让我们看看如何使用这些函数来调试我们之前定义的栈服务器。
{:ok, pid} = Stack.start_link([])
:sys.statistics(pid, true) # turn on collecting process statistics
:sys.trace(pid, true) # turn on event printing
Stack.push(pid, 1)
*DBG* <0.122.0> got cast {push,1}
*DBG* <0.122.0> new state [1]
:ok:sys.get_state(pid)
[1]Stack.pop(pid)
*DBG* <0.122.0> got call pop from <0.80.0>
*DBG* <0.122.0> sent 1 to <0.80.0>, new state []
1:sys.statistics(pid, :get)
{:ok,[start_time: {{2016, 7, 16}, {12, 29, 41}},current_time: {{2016, 7, 16}, {12, 29, 50}},reductions: 117,messages_in: 2,messages_out: 0]}:sys.no_debug(pid) # turn off all debug handlers
:ok:sys.get_status(pid)
{:status, #PID<0.122.0>, {:module, :gen_server},[["$initial_call": {Stack, :init, 1}, # process dictionary"$ancestors": [#PID<0.80.0>, #PID<0.51.0>]],:running, # :running | :suspended#PID<0.80.0>, # parent[], # debugger state[header: 'Status for generic server <0.122.0>', # module statusdata: [{'Status', :running},{'Parent', #PID<0.80.0>},{'Logged events', []}],data: [{'State', [1]}]]]}
相关文章:
Elixir GenServer
GenServer 是一个用来实现客户端-服务器模式中服务器的行为模块。 GenServer 是一个普通的 Elixir 进程,同其他 Elixir 进程一样,它可以用来保存状态、异步执行代码等。使用这个模块来实现通用服务器进程(GenServer)的优势在于&a…...
第八节、Bresenham直线插补【51单片机-TB6600驱动器-步进电机教程】
摘要:前面章节主要介绍单个电机控制,本节内容介绍两个电机完成直线插补运动 一、 Bresenham直线算法介绍 Bresenham直线算法由Jack Elton Bresenham于1962年在IBM开发,最初用于计算机显示直线,它确定应该选择的n维光栅的点&#…...
JDK1.8
JDK1.8 1. Lamdba表达式 Lambda表达式是什么? Lambda是一个匿名函数,我们可以将Lambda表达式理解为一段可以传递的代码(将代码像数据一样传递)。使用它可以写出简洁、灵活的代码。作为一种更紧凑的代码风格,使java语…...
【jvm】讲讲jvm中的gc
目录 1. 说明2. 主要算法2.1 标记-清除算法2.2 复制算法2.3 标记-整理算法3. 主要回收器3.1 Serial GC3.2 Parallel GC3.3 CMS(Concurrent Mark-Sweep)GC3.4 G1(Garbage-First)GC 4. 触发条件4.1 Minor GC(Young GC&am…...
Oracle 用户管理模式下的恢复案例-不完全恢复
1. 不完全恢复的几种常用方法 01. recover database using backup controlfile 如果丢失当前控制文件,用冷备份的控制文件恢复的时候,用来告诉 oracle,不要以 controlfile 中的 scn 作为恢复的终点; 02. recover database until …...
Leetcode经典题4--查找数组中的多数元素+Boyer-Moore 投票算法
题目描述: 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 输入输出示例 输入:nums [2,2,1,1,1,2,2] 输出…...
ubuntu20.04 如何使用 perf 性能分析工具 以及 hotspot 性能分析可视化工具?
官方文档:https://perfwiki.github.io/main/ 如何安装:https://askubuntu.com/questions/50145/how-to-install-perf-monitoring-tool 首先是安装 perf: sudo apt-get install linux-tools-common sudo apt-get install linux-tools-generic sudo apt-…...
Scala-隐式转换
系统自动完成的:把一种类型的数据转成了另一种类型的数据。 不是所有的类型都可以执行隐式转换 函数的参数类型 和 返回值类型是重要的,它约定了把什么类型转成什么类型 //张三的函数def getAge():Double{8.5}//函数名:tranform 不重要//函…...
基于Java和Vue开发的漫画阅读软件漫画阅读小程序漫画APP
前景分析 受众广泛:漫画的受众群体广泛,不仅限于青少年,还涵盖了成年人等多个年龄层和社会阶层。漫画文化在全球范围内的影响力不断扩大,未来漫画软件创业可以考虑全球市场的拓展。 市场需求大:数字化阅读趋势下&…...
租赁小程序的优势与应用场景解析
内容概要 租赁小程序,听起来是不是很酷?其实,它就是一个让你可以方便地租借各种高成本但用得不频繁的商品的平台。想象一下,当你需要租一件派对用的华丽小礼服,或是想体验一下超酷的运动器材,租赁小程序就…...
CC-Link IEFB转Modbus TCP协议网关(技术分享)
一,设备主要功能 捷米特JM-CCLKIE-TCP网关实现连接到CC-Link IE Field Basic总线中做为从站使用,连接到Modbus TCP总线中做为主站或从站使用。 应用广泛:捷米特JM-CCLKIE-TCP广泛应用于支持Modbus TCP接口的上位机、变频器、仪表、马保等等…...
开发手札:Win+Mac下工程多开联调
最近完成一个Windows/Android/IOS三端多人网络协同项目V1.0版本,进入测试流程了。为了方便自测,需要用unity将一个工程打开多次,分别是Win/IOS/Android版本,进行多角色联调。 在Win开发机上,以Windows版本为主版…...
SSM报错:表现层方法应该返回字符串,但是返回页面
在进行SSM项目时,后端表现层应该返回给前端字符串,但是却跳转页面 1.首先检查是否使用ResponseBody注解 ResponseBody注解 作用:将java对象转为json格式的数据。将controller的方法返回的对象通过适当的转换器转换为指定的格式之后࿰…...
Unity中使用Sqlite存储本地数据
sqlite-net sqlite下载页 我的环境:win11、unity团结1.3.4 1.下载sqlite-net,将SQLite.cs脚本导入Unity 2.下载各平台依赖项,如dll、aar等。导入Unity并设置 3.简单列子,打包测试 using System; using System.IO; using SQLi…...
微信直连:商户转账到零钱API开发报错:java.security.InvalidKeyException: Illegal key size
jdk版本 解决方案:下载jce_policy-8,替换jre下的 local_policy.jar 文件 替换jdk内的jre下的 替换jre下的 JDK 8u161 之后默认使用了不限制的Policy...
语言处理程序基础
逻辑运算 正规式 有限自动机 上下文无关文法 表达式(前缀、后缀、中缀) 将表达式(a-b)*(c5)构造成树的步骤为:括号不能出现在树中;按照表达式的计算顺序来依次构造!&…...
deepsort复现报错TypeError: tuple indices must be integers or slices, not tuple 解决
deepsort复现中遇到的TypeError: tuple indices must be integers or slices, not tuple问题的解决-CSDN博客 以上为参考教程。 复现的时候出现这个报错,搞了好久试了好多方法,包括降 scikit-learn版本,都不行,最后终于找到这个博…...
「Mac畅玩鸿蒙与硬件42」UI互动应用篇19 - 数字键盘应用
本篇将带你实现一个数字键盘应用,支持用户通过点击数字键输入数字并实时更新显示内容。我们将展示如何使用按钮组件和状态管理来实现一个简洁且实用的数字键盘。 关键词 UI互动应用数字键盘按钮组件状态管理用户交互 一、功能说明 数字键盘应用将实现以下功能&…...
MSSQL SQLi Labs靶场 第一关 (手工版)
一.判断是否存在注入 输入http://172.16.0.87/less-1.asp?id1页面异常 加上闭合点:http://172.16.0.87/less-1.asp?id1--页面正常,说明存在注入 二.判断数据库类型 可通过以下Payload来探测当前站点是否是MSSQL数据库,正常执⾏说明后台数据…...
重磅更新:CnosDB 2.3.5.4 版本上线, 性能提升,问题修复一网打尽
📢 重磅更新:CnosDB 2.3.5.4 版本上线, 性能提升,问题修复一网打尽 📢 我们很高兴地向大家介绍最新版本的更新,以下是本次更新的主要内容: 🔹 版本号:2.3.5.4 🔹 发布…...
后端-编辑按钮的实现
编辑一共要实现两步: 1.点击编辑蹦出来一个弹窗,此时需要回显,根据id查出来这条数据 2.修改某些值之后点击保存的时候调用修改的接口 根据id查询的时候正常操作 修改值的时候要注意一些问题 mapper层的Employee和impl层的接收实体不一样...
Python语言基础入门教程
Python是一种简单易学、功能强大的编程语言,非常适合初学者入门。本篇博客将带你从零开始,学习Python的一些基础知识,并通过详细分析和实例帮助你理解。 一、什么是Python? Python是一种高级编程语言,具有以下特点&am…...
verilog编程规范
verilog编程规范 文章目录 verilog编程规范前言一、代码划分二、verilog编码ABCDEFG 前言 高内聚,低耦合,干净清爽的代码 一、代码划分 高内聚: 一个功能一个模块干净的接口提取公共的代码 低耦合: 模块之间低耦合尽量用少量…...
Spring Boot 的启动原理
Spring Boot 是由 Pivotal 团队发布的一个开源框架,它基于 Spring 框架,旨在简化企业级应用程序的开发过程。与传统的 Spring 项目相比,Spring Boot 提供了一种更加简洁、高效的方式来构建和部署应用程序。其核心理念是“约定优于配置”和“自…...
Oracle 19C RU补丁升级,从19.7to19.25 -单机
1. 环境信息: 角色 数据库 IP地址 数据库版本 数据库名称 源端 单实例 172.30.21.191 19.7 hfzcdb 2. 安装准备 用rman备份数据库,再备份下oracle目录: 命令: tar zavf oracle.tar /oracle 19.25版本关于19C的补丁内…...
Web day08 项目实战(2)
目录 查询员工: 在EmpController层: 在pojo层: 在EmpServiceImpl 层中: 在dao层: 新增员工: pojo层: EmpController层: 在EmpServiceImpl 层中: 在EmpMapper层: …...
生命周期(vue2和vue3的生命周期对比)有哪些?
Vue.js 提供了一套完整的数据驱动和组件化思想,其生命周期钩子是开发者理解组件行为的关键。以下是 Vue 2 和 Vue 3 中生命周期钩子的对比: Vue 2 生命周期钩子 创建阶段 beforeCreate:实例初始化之前调用,此时 data 和 methods …...
智慧信息发系统——控件磁吸
//鍙悜涓婂惛 function fun_鎺т欢纾佸惛_璁$畻(鐏典綋y,寮曞姏鍊�,閫夋嫨鍖哄煙,璁$畻鍧愭爣绫诲瀷) {const 灞忓箷椤圭洰 document.querySelectorAll(閫夋嫨鍖哄煙);var 璺濈鎴戞渶杩憏 0;var 鏈€杩戜竴娆″紩鍔� 0…...
【时间序列预测】基于PyTorch实现CNN_LSTM算法
文章目录 1. CNN_LSTM模型概述2. 网络结构3. 完整代码实现4.模型解析4.1 CNN层4.2 ReLU层4.3 MaxPooling层4.4 LSTM层4.5 输出层4.6 前向传播 5. 总结 在时间序列预测任务中,CNN(卷积神经网络)和LSTM(长短期记忆网络)是…...
Android环境搭建
Android环境搭建 第一步:安装 Homebrew 执行以下命令来安装 Homebrew: /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"检测是否安装成功: brew --version第二步:安装 No…...
HarmonyOS 5.0应用开发——Ability与Page数据传递
【高心星出品】 文章目录 Ability与Page数据传递Page向Ability传递数据Ability向Page传递数据 Ability与Page数据传递 基于当前的应用模型,可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 使用EventHub进行数据通信:在基类Context中提供…...
【数据结构】顺序表
一、顺序表的基本概念 1.1 概念 顺序表是一种线性表的存储结构,其特点是:使用一段连续的存储空间存储线性表中的数据元素,通过数组实现,具有随机访问的能力。 可以把顺序表直接理解为数组,只不过这个数组里可以存各种类…...
光伏与储能软件有哪些?
随着技术的不断进步,光伏与储能的软件系统也层出不穷,为这一领域的发展提供了强有力的支持。 一、光伏设计软件 1、PVSyst 功能:这是一款全球广泛使用的光伏系统设计软件,支持光伏系统的模拟与设计,包括组件阵列、倾…...
AI周报(12.1-12.7)
AI应用-AI独立开发“小猫补光灯” 无论GitHub Copilot、Amazon CodeWhisperer,还是前面AI周报(6.23-6.29)Devv.ai,重在提高编码效率、提供编码补全和建议。小猫补光灯第一次让一个完全不会编程的产品经理,与AI协作&…...
windows 脚本批量管理上千台服务器实战案例
如果你们有接触服务器,都是知道服务器有BMC管理界面的,这几天我在做项目中,需要不断的开关机服务器,如果一两台服务器登录BMC界面重启服务器还好,如果服务器数量非常的庞大,成百上千台,我们不可…...
一级路由访问家里的二级路由设备 例如nas
家里升级千兆网,更换了光猫设备,家里走网线的话,只有100m速度,就直接用了光猫的无线,这次换的很顶,下载和打游戏也够用了,基本上没有网络波动。就客厅用了一个路由器,接了nas和摄像头…...
线程(二)——线程安全
如何理解线程安全: 多线程并发执行的时候,有时候会触发一些“bug”,虽然代码能够执行,线程也在工作,但是过程和结果都不符合我们的开发时的预期,所以我们将此类线程称之为“线程安全问题”。 例如ÿ…...
Altium Designer学习笔记 31 PCB布线优化_GND处理
基于Altium Designer 23学习版,四层板智能小车PCB 更多AD学习笔记:Altium Designer学习笔记 1-5 工程创建_元件库创建Altium Designer学习笔记 6-10 异性元件库创建_原理图绘制Altium Designer学习笔记 11-15 原理图的封装 编译 检查 _PCB封装库的创建Al…...
第四节、电机定角度转动【51单片机-TB6600驱动器-步进电机教程】
摘要:本节介绍用电机转动角度计算步骤,从而控制步进电机转角 一、 计算过程 1.1 驱动器接收一个脉冲后,步进电机转动一步,根据驱动器设置的细分值 计算一个脉冲对应电机转动的角度step_x s t e p x s t e p X … … ① step_{x…...
亚马逊云科技用生成式AI,向开发的复杂性动手了
生成式 AI、分布式扩展功能全面进化,还降价了。 同一天的发布,完全不同的方向。 今天凌晨,云计算巨头亚马逊云科技的 re:Invent 与大号创业公司 OpenAI 的发布「撞了车」。后者公布了一系列生成式 AI 应用,价格更贵、性能更强大&a…...
SharpDevelop IDE IViewContent.cs类
文件位置:IViewContent.cs /// <summary>/// IViewContent is the base interface for "windows" in the document area of SharpDevelop./// A view content is a view onto multiple files, or other content that opens like a document/// (e.…...
【工具变量】地级市城市全社会用电量数据(2006-2021年)
一、数据范围:覆盖中国300多个地级市 二、包含指标: 省份、地级市、年份、全社会用电量。 三、数据来源:国家电网查询数据。对于极大部分城市,国网售电量就是全社会用电量(往年的售电量和全社会用电量数据相同),此外…...
vue列表滚动动画效果
一、效果展示: 录屏2024-12-07 02.16.59 二、步骤: 1. 需要用<transition-group>包裹需要渲染的item列表 ⚠️注意:这里的:key"item.id",必须要用id之类的,不能用index <transition-group name&qu…...
33.5 remote实战项目之设计prometheus数据源的结构
本节重点介绍 : 项目要求 通过remote read读取prometheus中的数据通过remote write向prometheus中写入数据 准备工作 新建项目 prome_remote_read_write设计prometheus 数据源的结构初始化 项目要求 通过remote read读取prometheus中的数据通过remote write向prometheus中写…...
Ceph文件存储
Ceph文件存储1.概念:数据以文件的形式存储在存储介质上,每个文件都有一个唯一的文件名并存储在一个目录结构中。提供方便的文件访问接口,支持多种文件操作,如创建、删除、读取、写入、复制等。用于存储和管理个人文件,如文档、图片…...
力扣-图论-5【算法学习day.55】
目录 前言 习题 1.移除可疑的方法 后言 前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程(例如想要掌握基础用法,该刷哪些题?)我的解析也不会做的非常详细,只会提供思路和一些关键…...
Linux-音频应用编程
ALPHA I.MX6U 开发板支持音频,板上搭载了音频编解码芯片 WM8960,支持播放以及录音功能!本章我们来学习 Linux 下的音频应用编程,音频应用编程相比于前面几个章节所介绍的内容、其难度有所上升,但是笔者仅向大家介绍 Li…...
SQL复杂查询功能介绍及示例
文章目录 1. 多表连接(JOIN)功能介绍应用场景示例查询及初始表格customers 表(未查询前)orders 表(未查询前)INNER JOIN 示例LEFT JOIN 示例 2. 子查询(Subquery)功能介绍应用场景示…...
Python使用Selenium自动实现表单填写之蛇年纪念币蛇钞预约(附源码,源码有注释解析,已测试可用
Python实现纪念币预约自动填写表单 声明:本文只做技术交流,不可用代码为商业用途,文末有源码下载,已测试可用。 Part 1 配置文件改写(源码 有详细的注释说明 读取配置文件,自己组数据库,录入信息 配置文件 Part 2 主函数 每一期的xpath路径都不一样 所以需要提前去网站…...
快速掌握HTML
目录 1. HTML文件的基本结构* 前端开发工具搭建* vscode的三个插件* 编写第一个html代码* 快速生成代码框架 *html特殊字符2. 双标签2.1 标题标签 h12.2 段落标签 p2.3 格式化标签2.4 超链接标签 a2.5 表格标签2.6 列表标签1. 无序列表:ul标签( 快捷键:u…...