Clojure代理是一个强大的工具。由于使用函数“send”和“send-off”异步发送对代理的操作,理论上不会发生类似死锁的事情。
是否可以使用我们遇到并发问题的代理编写一些Clojure代码(例如从某个操作调用另一个代理到另一个代理) - 它可能是死锁,竞争条件或其他任何东西。
答案 0 :(得分:9)
send
和send-off
将函数添加到代理的队列并立即返回。因此,由于它们的异步性质,它们不可能死锁。
但是,send
使用的工作池用于计算工作,因此是固定池大小(2 +核心数)。如果您(错误地)使用可以阻止I / O或其他任务的任务调用send
,该任务将阻止池中固定数量的cpu线程之一。如果您同时使用(2 +#个内核)任务,则可以有效地阻止cpu线程池。当然,解决方案是使用正确的send-off
。 send-off
使用的工作池是无限制的。
在动作中发送时,agents页面声明:
如果在执行功能期间进行任何其他调度 (直接或间接地),他们将被保留到状态之后 代理已被更改。
当调度发生在代理的末尾时,发送将项放在发送队列中并立即返回。因为代理从不阻止等待资源,所以无法创建死锁循环。
数据竞争也不可能 - 代理的状态应该是不可变的Clojure数据。代理在发送中应用更改功能,并以原子方式替换代理的状态。读者(通过dereference
或@
)在代理生命周期的某个时刻看到一个稳定的值。如果您需要协调读者和作者,则必须使用refs。
有可能存在竞争条件:来自不同线程的两次send
调用可以“竞争”放入代理队列中,并且并发读者可能会感觉到不同(稳定)值取决于他们何时阅读代理的时间表。但是,这是异步计算的本质。如果在读取或写入多个资源时需要协调计算,则必须使用refs。
答案 1 :(得分:2)
我不确定这是否可行,因为“发送”调用的异步性质可以防止出现这种死锁问题。如果send是同步的,那么很容易就可以在send函数中再次调用send到同一个代理,现在如果它是同步的,send会被卡住。