Erlang select receive机制细节及实操结果不符问题咨询
Erlang 选择性接收(select receive)机制实现差异说明
核心差异原因
你复现结果和旧文章结论不一致的核心原因是Erlang/OTP版本迭代优化了内部实现:2010年相关文章的结论仅适用于R14B及更早的旧版本Erlang,OTP 17之后官方重写了选择性接收的逻辑,你使用的OTP 21已经是优化后的实现版本。
旧版本实现逻辑(R14B及更早)
旧版本确实遵循你看到的结论:
- 执行
receive匹配时,进程会遍历消息队列,将不匹配的消息临时移动到独立的「保存队列」 - 直到匹配到目标消息、或
receive触发超时后,再把保存队列里的所有消息移回主消息队列 - 这个阶段用
process_info(..., messages)查询时,看不到被移到保存队列的消息,和你测试的结果不同
新版本实现逻辑(OTP 17及以后,包含你用的OTP 21)
官方针对旧版本消息反复移动的性能问题做了优化,不再实际移动消息:
- 进程扫描消息队列找匹配项时,只会用指针标记已经扫描过的不匹配消息位置
- 新消息到达后,直接从上次标记的位置往后扫描即可,不需要挪动任何已收到的消息
- 所有未消费的消息始终留在主消息队列中,所以你用
process_info查询时,始终能看到c、d两条消息,队列长度也始终为2,和你测试的结果完全吻合
你的测试场景对应说明
你的测试操作符合新版本实现的正常表现:
% shell1操作 (aaa@HW0003727)1> register(shell, self()). true (aaa@HW0003727)2> shell ! c, shell ! d. d (aaa@HW0003727)3> process_info(whereis(shell),messages). {messages,[c,d]}. (aaa@HW0003727)4> receive a -> 1; b -> 2 end. % shell2操作 (aaa@HW0003727)1> process_info(whereis(shell),messages). {messages,[c,d]} (aaa@HW0003727)2> process_info(whereis(shell)). [{registered_name,shell}, {current_function,{prim_eval,'receive',2}}, {initial_call,{erlang,apply,2}}, {status,waiting}, {message_queue_len,2}, {links,[<0.113.0>]}, {dictionary,[]}, {trap_exit,false}, {error_handler,error_handler}, {priority,normal}, {group_leader,<0.112.0>}, {total_heap_size,4212}, {heap_size,1598}, {stack_size,30}, {reductions,13906}, {garbage_collection,[{max_heap_size,#{error_logger => true,kill => true,size => 0}}, {min_bin_vheap_size,46422}, {min_heap_size,233}, {fullsweep_after,65535}, {minor_gcs,1}]}, {suspending,[]}]
你执行receive a -> 1; b -> 2 end.后进程挂起等待匹配消息,因为没有匹配项,两条不匹配的消息没有被移动,所以shell2查询时始终能看到完整的消息队列,属于新版本的正常表现。
内容的提问来源于stack exchange,提问作者Caker Pang
相关产品推荐
相关产品推荐

