Concurrency Prolog中并发性的抽象

Concurrency Prolog中并发性的抽象,concurrency,erlang,prolog,actor,erlog,Concurrency,Erlang,Prolog,Actor,Erlog,我们正在构建Erlog的1.0版本,这是一个可以在Erlang进程中运行的prolog。目前,我已经实现了通过普通erlang机制发送和接收消息的功能。然而,我希望能够在erlang的消息传递之上向Erlog添加其他并发抽象 那么,人们发现在Prolog或LP编程环境中还有哪些并发抽象是有用的呢 tl;dr 记下OTP提供的主要抽象,仔细记下您认为经常重复的情况,并在代码中开发一个标准答案,抽象出以前存在的任何特殊方法。(我想这听起来很像重构。) 更长的答案 只要进程足够便宜,以至于程序员永远不

我们正在构建Erlog的1.0版本,这是一个可以在Erlang进程中运行的prolog。目前,我已经实现了通过普通erlang机制发送和接收消息的功能。然而,我希望能够在erlang的消息传递之上向Erlog添加其他并发抽象

那么,人们发现在Prolog或LP编程环境中还有哪些并发抽象是有用的呢

tl;dr

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

更长的答案

只要进程足够便宜,以至于程序员永远不会考虑它们的成本,并且消息传递是共享数据的唯一方法,那么您就有了高点。然而,基于这些简陋的开端,一些其他模式通常会出现,因为它们显然有助于抽象

您可能会注意到的第一件事是,您编写了多少特别的方法来确保同步调用的安全。作为一个非常简单的示例,您可以执行以下操作(Erlang):

或者别的什么。无论您以何种方式开始执行同步调用(无监控方式或监视器演示器响应方式),关键是您应该将该模式至少封装为一个类似于上面的
ask/4
的函数,而不是到处创建临时同步调用例程。(我对原始Erlang项目的一个主要抱怨是,它们常常太晚才发现需要同步调用抽象方式。)

这个特殊的问题是gen_server的核心所在——处理将原始Erlang代码丢弃在“当时似乎是个好主意”内联过程中的常见情况,并将它们抽象为服务进程通常做什么的单个概念

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

除了实现gen_server之类的东西之外,我建议至少实现一个类似于OTP的gen_fsm的有限状态机抽象。我发现OTP的这一部分在很多情况下比gen_服务器更有用,在亲手编写了100个fsm之后,我真的很欣赏gen_fsm处理大部分事务的实用性(它仍然保留了处理信息的
handle\u info
,这是非常有用的,因为不管怎样,这都是你不得不通过黑客手段实现的)


OTP的抽象通常在过程层面工作。考虑什么样的系统可能是有帮助的,哪些系统不是单一的过程。例如,我一直在与同行监督的系统玩一点,并且显然有一些模式出现在那里。如果我进一步追问,我会孤立我所学的课程。关于这些系统是如何工作的(比如需要一种方法来分类癌症过程等),我可能需要将Erlang行为和形式语法混合起来来描述这样一个系统,这样我就可以停止编写过程代码,转而关注我感兴趣的问题的级别。

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

actors receive有点原始,因为它总是使用匹配的消息队列条目。因此,例如,如果您想要调用,您可能需要向Erlang添加一个peek,它返回消息队列的副本条目,但仍保持消息队列不变

否则,必须按如下方式解决这一问题,将未来推回到队列上。同时,isDone()调用必须由拥有消息队列的参与者进行调解。因此,Java示例实际上不会转换为直接调用isDone(),如下所示,在实践中会更加复杂:

isDone(Future) :-
    receive 
       Future  -> self(Pid), Pid ! Future;
       after 0 -> fail.
附言:您现在可以使用基于UDP的消息b
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.
isDone(Future) :-
    receive 
       Future  -> self(Pid), Pid ! Future;
       after 0 -> fail.