我观察到C NIF的阻塞行为,当它们被许多Erlang进程同时调用时.可以做成非阻塞吗?这里有mutex
工作,我无法理解吗?
PS一个基本的"Hello world"NIF可以通过在特定调用它的情况下使其成为sleep
一百来进行测试.可以观察到,调用NIF的其他PID等待该睡眠在执行之前执行.microseconds
PID
在并发可能不会造成问题的情况下(例如,数组推送,计数器增量),非阻塞行为将是有益的.
我共享链接至4个要旨,其包含的spawner
,conc_nif_caller
和niftest
分别模块.我试图修改价值,Val
我确实观察到了非阻塞行为.通过为spawn_multiple_nif_callers
函数指定一个大整数参数来确认这一点.
链接 spawner.erl,conc_nif_caller.erl,niftest.erl,最后是niftest.c.
下面的行由我的Mac上的Erlang REPL打印.
Erlang/OTP 17 [erts-6.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
mpm.. 5
NIF本身没有任何互斥锁.您可以在C中实现一个,并且在加载NIF的对象时有一个,但这应该只使用加载模块执行一次.
可能发生的一件事(我敢打赌这是发生了什么),是你的C代码搞砸了Erlang调度程序.
在返回之前执行冗长工作的本机函数会降低VM的响应性,并可能导致各种奇怪的行为.这种奇怪的行为包括但不限于极端的内存使用,以及调度程序之间的负载均衡.由于冗长的工作可能发生的奇怪行为也可能因OTP版本而异.
和描述的是什么lengty work
意思,你是如何解决的.
用极少的话说(简化很少):
对于核心,创建了一个调度程序.每个都有一个他可以运行的进程列表.如果一个调度程序列表为空,他将尝试继续使用另一个调度程序列表.如果没有(或者不够)仍然存在,这可能会失败.
Erlang调度程序在一个进程中花费了大量的工作,而不是移动到另一个进程,花费一些工作量,然后转移到另一个进程.等等,等等.这与系统进程中的调度非常相似.
这里非常重要的一件事是计算工作量.默认情况下,每个函数调用都分配了一些减少量.添加可能有两个,你的模块中的调用函数将有一个,发送消息也一个,一些内置可能有更多(如list_to_binary
).如果我们收集2 000个减少量,我们会转移到另一个流程.
那么C函数的成本是多少?这只是一次减少.
代码就像
loop() -> call_nif_function(), loop().
可能需要整整一个小时,但调度程序将停留在这一个过程中,因为他仍然没有计入2 000减少.换句话说,他可能被困在NIF内部而无法前进(至少在很短的时间内).
围绕这一点的方法很少,但一般规则是统计NIF不应该花很长时间.因此,如果你有很长的C代码,也许你应该使用驱动程序.他们应该更容易实施和管理,修补NIF.