Prolog中的并发抽象

时间:2014-10-12 04:12:54

标签: concurrency erlang prolog actor erlog

我们正在努力构建Erlog​​的1.0版本,这是一个可以在Erlang进程中运行的序言。现在我已经实现了通过正常的erlang机制发送和接收消息的能力。但是,我希望能够在Erlang的消息传递之上添加其他并发抽象到Erlog​​。

那么人们发现其他并发抽象在Prolog或LP编程环境中有用吗?

3 个答案:

答案 0 :(得分:1)

<强> TL;博士

注意OTP提供的主要抽象,故意记录您认为经常重复的情况,并在代码中开发一个标准答案,以抽象出以前存在的任何临时方法。 (听起来很像重构,我想。)

更长的答案

只要进程足够便宜而程序员从不考虑其成本,并且消息传递是共享数据的唯一方法,那么你就可以获得高点。然而,基于那些不起眼的开头,通常会出现一些其他模式,这些模式显然对抽象有用。

您可能会注意到的第一件事是您经常使用临时方法来使同步调用安全。作为非常浅薄的示例,您可以执行类似(Erlang)的操作:

ask(Proc, Request, Data, Timeout) ->
    Ref = make_ref(),
    Proc ! {self(), Ref, {ask, Request, Data}},
    receive {Ref, Res} -> Res
    after Timeout -> {fail, timeout}
    end.

ask(Proc, Request, Data, Timeout) ->
    Ref = monitor(process, Proc),
    Proc ! {self(), Ref, {ask, Request, Data}},
    receive
        {Ref, Res} ->
            demonitor(Ref, [flush]),
            Res;
        {'DOWN', Ref, process, Proc, Reason} -> {fail, Reason}
    after Timeout -> {fail, timeout}
    end.

或其他什么。你开始做同步调用的方式(不受监控的方式或者监视器 - 恶魔 - 响应方式)并不重要,关键是你应该将该模式包装到至少类似的单个函数中到ask/4以上,而不是在整个地方创建临时同步调用例程。 (我对Erlang原始项目的一个主要抱怨是他们经常发现需要同步调用抽象的方式太晚了。)

这个特殊问题是gen_server的核心所在 - 处理乱丢原始Erlang代码的常见情况“当时看起来像个好主意”内联程序并将它们抽象为一个单一的概念用于什么服务过程一般都是。

当你有两个相同的进程同时发出信号时,你几乎不可避免地要开发一个避免死锁的通用模式。我这样做的典型方法是生成一个中介来处理它,一些系统永远不会让两个相同定义的进程直接对话(一种通用的仲裁),有时你只会看到“超时并返回到状态X”或者其他。并非每种处理潜在死锁的方法都适用于每个系统,但在设计并发系统时,这是您将反复处理的内容。将你发现死锁的情况分类并抽象出你用来避免它们的方法,这不是一个坏主意。

除了实现像gen_server这样的东西之外,我建议至少实现类似于OTP的gen_fsm的有限状态机抽象。在很多情况下,我发现OTP的这一部分比gen_server更有用,并且在手工编写了一百个fsm后,我真的很感激gen_fsm处理了大部分内容(并且它仍然留下handle_info,这是非常有用的,因为无论如何你都不得不以某种方式强行实施。)

OTP的抽象通常在流程层面起作用。考虑可能实例化哪种非单一进程的系统也可能有所帮助。例如,我一直在玩同伴监督系统,显然有一些模式出现。如果我进一步追求这一点,我将把我正在学习的关于这些系统如何工作的课程(比如需要一种分类癌症过程的方法等)分离成可能是Erlang行为和形式语法的混合来描述这样一个系统所以我可以停止编写流程代码,而是专注于我感兴趣的问题的级别。

答案 1 :(得分:1)

一些Prolog系统(例如Ciao,Qu-Prolog,SWI-Prolog,XSB和YAP)实现了多线程编程。这些实现通常会提升低级pthreads库(至少在POSIX操作系统中)。除了线程,这些系统中的一些还提供互斥和消息队列。使用这些基本块,可以轻松实现的两个有用的抽象是竞争性或并行性独立性和并行性。在竞争或并行中,一组目标同时进行,直到其中一个成功(杀死其他目标)。一个用法示例是尝试使用一组启发式解决问题而不知道每个启发式将更快地提供可接受的答案。在独立和并行中,一组目标同时运行并被解释为连接。目标不共享变量(具有完全熟知的异常,其中共享对于并发是安全的)并且所有这些变量必须成功。一个用法示例是通过将其分解为一组子问题来解决问题,其中每个子问题必须成功解决。您可以在例如以下找到这些抽象的实现。 Logtalk(使用SWI-Prolog,XSB或YAP作为后端编译器运行时)和SWI-Prolog库。 Logtalk发行版提供了几个使用这些抽象的例子。

答案 2 :(得分:0)

actor接收的消息有点原始,因为它总是消耗匹配的消息队列条目。因此,例如,如果您想进行Future.isDone()调用,您可能需要向Erlang添加一个窥视器,该窥视器确实返回消息队列条目的副本,但仍然保持消息队列不变。

否则,必须按以下方法解决此问题,将未来推回队列。同样,isDone()调用也必须由拥有消息队列的actor进行调解。因此,Java示例并未真正转换为如下所示的isDone()的直接调用,并且在实践中会更加复杂:

panTo

P.S .:您现在可以在Jekejeke Prolog中使用基于UDP的消息代理进行播放:
https://gist.github.com/jburse/253739554305af625e746366cff9b66b#gistcomment-3089863