《gen_server.erl源码》

简介: erlang程序员研究OTP,如同C++程序员研究STL一样重要。

OTP源码分析。

erlang程序员研究OTP,如同C++程序员研究STL一样重要。

今天原本想看看OTP的源码,google了一把,有个家伙的图画的实在是赏心悦目。于是,稍做编辑,照搬了过来。

init的调用顺序

为什么从gen_server它开始, 因为gen_fsm和它很类似, 而supervsisor本身是一个gen_server.

弄清楚init函数调用时机非常重要:是一个同步调用!上层调用要同步等待子进程被创建出来,并且成功的执行完了init函数。

init

0c1937ba1126ba4e9cd7e8499e494616a368b7ea.png

图示为一个叫Mod的模块, 它是一个gen_server程序, 绿色方格为调用进程(客户进程), 黄色方格为spawn出的gen进程(服务进程). 不同的泳道表示函数所隶属的模块, 通过这个图可以清晰的看出各个模块至之间的相互调用, 图是使用gliffy所画。

从左上角的start(Args)开始,gen_server的程序Mod的开始函数都会调用gen_server:start(或者start_link)来创建一个服务进程,gen_server:start内部实际上调用的是gen模块。

gen模块是很多behaviour的基础,对于gen进程的启动和同步操作进行了封装.gen模块使用proc_lib:start来更加安全的spawn出gen进程,然后阻塞在sync_wait这个函数里,等待gen进程的回应.

spawn出的gen进程会执行到Mod模块的init函数。然后会把{ok, Pid}发送给调用进程告知gen进程已经完成准备工作,然后就进入了自己的循环函数loop中,等待调用进程的下次消息。

调用进程接收到{ok,Pid}之后从阻塞里退出并返回。

整个过程简单,但是要清楚哪个函数是被调用进程或者gen进程所执行。

另外要注意start函数的参数是Args,而init函数的入参是[Args],这个很容易出错的地方。

可以调用start/4或者start_link/4给启动的gen进程命名,函数name_register实际调用register函数.

init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
    case name_register(Name) of
    true ->
        init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options);

cast调用以及返回值

cast:这是个异步的调用,不会阻塞。没有返回值。

6b3e8757ef6d036daae59e272285e78e5e457770.png

调用进程向gen进程发送cast消息,消息发送之后调用进程并不等待gen进程的消息回馈.

gen进程从loop函数处接受到Request消息,模式匹配后一路执行到Mod:handle_cast函数,处理消息之后,gen进程继续递归执行loop函数等待后续的新消息. 注意handle_cast不能返回{reply,…},否则gen进程会报错退出。

call调用以及返回值

call:这是个同步的调用,会阻塞。同时会得到gen_server:handle_call的返回值。

cbf57b4aa21837ca4917f5278e627c12a9b0bb27.png

调用进程向gen进程发送call消息,和cast不同,调用进程会阻塞在wait_resp_mon函数里等待gen进程的回馈。收到消息后,gen进程会执行Mod:handle_call函数,并把执行的结果Reply直接发送给调用进程,然后自己再次进入循环等待新的消息。

wait_resp_mon(Node, Mref, Timeout) ->
    receive
    {Mref, Reply} ->
        erlang:demonitor(Mref, [flush]),
        {ok, Reply};
    {'DOWN', Mref, _, _, noconnection} ->
        exit({nodedown, Node});
    {'DOWN', Mref, _, _, Reason} ->
        exit(Reason)
    after Timeout ->
        erlang:demonitor(Mref),
        receive
        {'DOWN', Mref, _, _, _} -> true
        after 0 -> true
        end,
        exit(timeout)
    end.

默认情况下handle_call的返回是{reply,….}. 而调用进程阻塞在wait_resp_mon的默认超时时间是5s(-define(default_timeout, 5000)).

在spec里看到handle_call的返回值可以是{noreply,…}, 或者gen进程在处理其它事情而达到超时时间, 则调用进程会异常退出, 你也可以在调用gen_server:call/3来设置一个call命令的超时时间.

对于call和cast的命令既可以使用Name也可以使用Pid, gen内部会进行Name到Pid的转化。

call(Name, Label, Request, Timeout)
  when is_atom(Name), Timeout =:= infinity;
       is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
    case whereis(Name) of
    Pid when is_pid(Pid) ->
        do_call(Pid, Label, Request, Timeout);

info调用以及返回值

info:带外消息,除了消息的格式,其余的行为和cast一样。异步的调用,不会阻塞。没有返回值。

image.png

可以给gen进程发送任何格式的信息,这类信息没有gen标签,gen进程接收到这类消息会调用gen_server:handle_info来处理消息,处理过程与cast消息一样都不能反馈结果给调用进程,所以如果handle_info返回{reply,…}也会导致gen进程报错退出。

terminate调用以及返回值

terminate:异步。

image.png

在handle_*等回调函数处都可以返回{stop,…} 这样使得gen进程执行Mod:terminate, 进行进程退出前的收尾工作, 然后回到主循环gen进程再退出.

目录
打赏
0
0
0
0
5
分享
相关文章
浅谈mysql数据库迁移至国产化达梦数据库
项目要求mysql数据库数据需要转到达梦数据库,对于达梦数据库的了解尚且不多,一开始使用手动转SQL脚本,效率极低,非常容易出错。达梦数据库的资料实在有限,经过后期研究,发现原来DM已经有自己的数据迁移工具,使用之后非常方便。对mysql数据库转达梦数据库的操作流程做一个简要分享。
5948 0
浅谈mysql数据库迁移至国产化达梦数据库
6 张图带你彻底搞懂分布式事务 XA 模式
XA 协议是由 X/Open 组织提出的分布式事务处理规范,主要定义了事务管理器 TM 和局部资源管理器 RM 之间的接口。目前主流的数据库,比如 oracle、DB2 都是支持 XA 协议的。
13180 1
6 张图带你彻底搞懂分布式事务 XA 模式
Debian 11 x64 安装 MySQL 8.0.33
本文提供了一个逐步指南,介绍了如何在 Debian 11 x64 上下载和安装 MySQL 8.0.33。此外,还提供了允许远程访问的说明。
2146 1
Debian 11 x64 安装 MySQL 8.0.33
排队免单小程序开发模式案例
排队免单小程序通过线上排队系统,为用户提供便捷的免单机会。主要功能包括用户注册与登录、商家入驻与管理、排队系统、通知与提醒、活动记录与查询。技术实现涉及微信小程序原生开发框架、后端技术、API接口和第三方服务。开发过程还包括全面的测试与优化,确保稳定运行和良好体验。最后,通过提交审核、上线运营和推广策略,吸引更多用户和商家入驻。
量子计算的现状与未来:从实验室到商用
【10月更文挑战第29天】量子计算正从理论探索迈向实际应用,代表了计算技术的革命性突破。本文探讨了量子计算的现状、挑战、未来趋势及商用潜力,包括药物研发、金融工程、大数据和人工智能等多个领域的应用前景。尽管面临量子比特稳定性、错误率控制等挑战,但量子计算有望成为推动科技革命的关键力量。
超分辨率相关的开源项目
该文档介绍了多种超分辨率模型及其GitHub项目地址,包括Real-ESRGAN(优化真实图片质量)、RCAN(基于残差结构与通道注意力机制)、SwinIR(基于Swin Transformer的图像恢复)、FSRCNN(轻量级快速超分辨率)、EDSR(增强型深度残差网络)、SRGAN(利用GAN的超分辨率模型)及LapSRN(多级Laplacian金字塔超分辨率)。
"SQL老司机大揭秘:如何在数据库中玩转数组、映射与JSON,解锁数据处理的无限可能,一场数据与技术的激情碰撞!"
【8月更文挑战第21天】SQL作为数据库语言,其能力不断进化,尤其是在处理复杂数据类型如数组、映射及JSON方面。例如,PostgreSQL自8.2版起支持数组类型,并提供`unnest()`和`array_agg()`等函数用于数组的操作。对于映射类型,虽然SQL标准未直接支持,但通过JSON数据类型间接实现了键值对的存储与查询。如在PostgreSQL中创建含JSONB类型的表,并使用`->>`提取特定字段或`@>`进行复杂条件筛选。掌握这些技巧对于高效管理现代数据至关重要,并预示着SQL在未来数据处理领域将持续扮演核心角色。
165 0
存储空间紧张?来看 TDengine TSZ 压缩算法如何显著提升压缩率
本篇文章中,我们将就如何在 TDengine 中开启 TSZ 压缩算法进行详细说明,并会针对 TSZ 压缩算法展开功能测试,为大家验证其在实际业务场景中的更优性能。
452 3
深入浅出Presto:大数据查询引擎的原理与应用
【4月更文挑战第7天】Presto是高性能的分布式SQL查询引擎,专为大规模数据交互式分析设计。它采用分离式架构,内存计算和动态规划优化查询,支持跨源查询、交互式查询和ANSI SQL兼容性。应用于大数据分析、实时数据湖查询和云原生部署。Presto的灵活性和效率使其在大数据处理领域备受推崇,适合分析师、数据科学家和IT架构师使用。未来将在博客中分享更多实践和案例。
1338 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问