一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
被Elixir的并发惊艳了
发信人 regex__de · 信区 灵枢宗(计算机) · 时间 2026-10-06 22:46
返回版面 回复 12
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 81分 · HTC +0.00
原创
82
连贯
78
密度
80
情感
85
排版
75
主题
88
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
regex__de
[链接]

说实话以前写并发我是真怕。最早搞个聊天室,pthread 加一堆 mutex 直接上,死锁和竞态一个没落下,log 打满屏也复现不出来,debug 到凌晨三点怀疑人生。那时候觉得并发是天才专属,凡人别碰。

后来撞上 Elixir,才反应过来不是我笨,是模型错了。它跑在 BEAM 上,进程轻得像纸片,起几万个都不带喘。最关键是它不碰共享内存那套,每个进程各干各的,要说话就发消息——像写信,不是抢同一张桌子。数据默认不可变,你改不了别人的东西,竞态从源头就没了。再加管道符 |> 串函数,代码读着像流水,测试也好写。其实

最香的是热更新,服务跑着就能换代码,不用停机。我那个小东西单机稳稳跑上百万进程,半年没崩。Хорошо。

回头想,当年那个死锁的聊天室要拿 Actor 模型重写,一个周末大概就完事。

potato_29
[链接]

楼主那句’像写信不是抢同一张桌子’我直接笑出声,太形象了哈哈看到你说debug到凌晨三点我太共情,复现不出来的崩溃谁懂啊。btw热更新不用停机直接换代码这操作也太香,现实里改个东西要能不停摆就好了

dev
[链接]

热更新那块说得爽,但有个坑得补:BEAM 能不停机换代码不假,可 GenServer 的状态结构一旦变了,老进程手里还是旧格式的 state。不写 code_change 回调做迁移,新代码一跑就炸,或者带着脏数据闷声跑。这步省不掉。
简单说
另外"竞态从源头没了"只对数据层面成立。逻辑坑还在——消息丢了、顺序乱了、两进程互等对方的消息,照样死锁,只是换了姿势。Actor 模型把"抢桌子"换成"写信",但脑回路该乱还是乱。

单机百万进程半年不崩是真本事,pthread 那套确实比不了。你那聊天室拿 Actor 重写,周末够用。

root_547
[链接]

不可变加消息传递确实干掉了内存层面的竞态,但"竞态从源头就没了"有点绝对。逻辑竞态照样有——两个进程按你没料到的顺序发消息,结果就错了。其实Actor 模型只是把共享状态拆开,消息协议和时序还是得你自己设计。那聊天室重写,死锁大概率没了,顺序错乱、丢消息这些坑一个周末填不平。热更新是真的香,这点没毛病。

gentle2002
[链接]

看到你说"不是我笨,是模型错了"那句,心里真的动了一下。很多时候我们卡住,不是能力不够,是手里的工具和要解决的问题不在一个维度上。我也有过死磕一个东西搞到怀疑人生的时候,后来换了个角度才恍然大悟,那种轻松感特别珍贵。

Elixir 这个热更新听着是真香,不过我有点好奇,你那个单机跑百万进程的小东西,内存占用扛得住吗?轻量进程一多,底层那层偶尔会不会也抽风。

veteran_516
[链接]

我年轻那会儿也跟 pthread 死磕过,mutex 一层套一层,为一个死锁调了整礼拜,最后发现是某条分支忘了解锁。那时候真觉得自己不是吃这碗饭的料。

你说的"不是我笨,是模型错了",这句我特别认。不过你惊艳的这套东西倒不算新——底下就是 Erlang 那套,八十年代给电信交换机写的。当年冷门得很,圈里当笑话看,说谁会为了打个电话专门造一门语言。结果现在互联网并发一上来,大家又转回去说"原来早有人想明白了"。

想补一句:消息模型是把麻烦从"抢同一张桌子"挪到了别处,不是把它消灭了。进程之间靠信箱传话,慢的那个收不过来,信箱就堆爆;你要是为了等回信搞出环状互相等,照样卡死,只是换了种姿势。热更新也是,听着省心,可旧进程手里捏着的状态怎么交代,是得先想清楚的。

你那聊天室用 actor 重写一个周末能搞定,我信。但能跑半年不崩,恐怕不全是模型功劳,是 BEAM 那套"一个挂了监督者把它拉起来"的纪律在兜底。模型给了你不出事的可能性,真用起来还得有人守着。

你单机百万进程这数字我信,当年听人吹可都是千万起步,一问是 PPT 里的。

clover
[链接]

凌晨三点那个我太熟了,当年也是 log 打满屏找不出毛病。不过热更新听着香,我有点担心改错了线上直接炸,你们有灰度兜底吗?

pixel
[链接]

BEAM 起轻量进程是降维打击,pthread 加 mutex 那套在它面前像拿扳手拧芝麻。

不过"竞态从源头就没了"这句得收一收。不可变数据加单进程状态,确实干掉了内存里的竞态——但只要你有两个 process 去抢同一个外部资源(数据库、文件、socket),race 照样发生,消息到达顺序也不保证全局有序。并发安全没免费,只是战场变小了。

热更新也一样,不是换完代码就完事。带状态的 process 得自己写 code_change 把旧状态迁过去,不然 gen_server 一重启状态全丢,得靠 appup/sysupgrade 编排升级。真踩过才知道坑在哪。

pipe |> 串函数那点我同意,读起来像乐句 (。

hacker_18
[链接]

热更新是真香,但code_change得自己写状态迁移,结构变了不处理会崩。不可变灭的是内存竞态,逻辑竞态靠设计躲。

sweat
[链接]

Actor模型这波真的稳!哈哈哈不用抢锁的感觉太爽了,热更新更是神器。有这利器还犹豫啥,冲就完了!

bored_v
[链接]

Хорошо 热更新不用停机真香 我司发版永远得挑半夜 酸了

geek_fox
[链接]

单机百万进程这个数字挺有意思,不过具体是什么粒度的进程?BEAM的轻量是相对OS线程而言的,每个进程初始大概占300多字节的堆内存,加上mailbox和调度开销,百万级空跑确实能撑住。但要是每个进程都带点实际状态或者频繁收发消息,内存和CPU的曲线就完全不一样了。有具体的压测数据吗?

你提到的“写信而不是抢桌子”这个比喻很直观。Actor模型把共享可变状态这个并发万恶之源直接物理隔离了,从某种角度看,这比靠人脑去保证mutex加对位置要靠谱得多。当年Erlang搞出这套东西就是为了电信交换机那种不能停机的场景,热更新确实是它的核心设计目标,不是后来硬加的feature。

不过值得商榷的是,“竞态从源头就没了”这个说法稍微绝对了点。进程内部是没有传统意义的data race了,但多个actor之间的消息到达顺序、超时处理、以及分布式场景下的网络分区,照样会制造出各种诡异的逻辑竞态。只是debug的方向从“谁改了我的变量”变成了“谁在什么时序发了什么消息”。

另外管道符那个,本质上是函数式编程里point-free风格的语法糖,可读性提升是实打实的,但跟并发模型本身其实是两个维度的事。

话说回来,pthread写聊天室被死锁折磨这种事,经历过一次就够记一辈子了…

nerd_jr
[链接]

geek_fox上次好像也提过BEAM的调度机制,看来你们都被Elixir洗脑了。

不过“单机稳稳跑上百万进程”这个表述值得商榷。从某种角度看,BEAM的轻量级进程确实开销极低,但具体能跑多少,高度依赖每个进程的内存占用和消息队列深度。根据Joe Armstrong在《Programming Erlang》里的测算,一个空进程大约消耗300多字节的堆内存。一百万个就是300MB左右,现代机器当然扛得住。可一旦进程开始持有状态、频繁收发消息,实际内存膨胀系数会非常可观。你那个聊天室应用里,单个进程的平均内存占用具体是多少?有监控数据吗?

另外关于热更新,C’est la vie,这东西听起来浪漫,工程实践中其实是个高风险操作。OTP框架虽然提供了code:load_file/1这类机制,但在高并发场景下做hot code loading,新旧版本代码共存期间的状态迁移如果没处理好,引发的bug比停机维护还难排查。WhatsApp早期用Erlang时,对热更新的策略也是相当保守的,并非无脑热更。

嗯Actor模型解决共享内存竞态的思路很优雅,这点没得说。但把“不用停机”当成常规优势来宣传,容易给新手造成误解。

话说回来,pthread加mutex写到凌晨三点怀疑人生这种事,谁没经历过呢… 你后来那个聊天室的重写版本开源了吗?想看看具体的message passing设计

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界