我应该如何让我的解析器并发?

Dav*_*ood 1 multithreading clojure reference-type concurrent-programming

我正在Clojure中实现一个音乐编程语言解析器.我们的想法是使用文本文件作为命令行参数运行解析器程序; 文本文件包含我正在开发的这种音乐语言的代码; 解析器解释代码并确定声明了什么"工具实例",并且对于每个工具实例,它解析代码并返回一系列音乐"事件"(音符,和弦,休止符等). .所以在最后一步之前,我们有多个"音乐代码"字符串,每个乐器实例一个字符串.

我对Clojure有点新,并且仍在学习如何使用引用类型和线程/并发的细微差别.我的解析器将进行一些复杂的解析,因此我认为使用并发来提高性能会受益.这是我的问题:

  1. 看来,最简单的方法是在初始解析(单线程操作)将文档"拆分"之后保存并发性,然后同时在不同的线程上解析每个工具的代码(而不是等到每个乐器完成解析,然后再移动到下一个).我是在正确的轨道上,还是有更有效和/或逻辑的方式来构建我的"并发计划"?

  2. 对于如何实现此并发解析,以及从性能或代码维护的角度来看,哪个可能最佳?看起来它可能很简单:(map #(future (process-music-code %)) instrument-instances)但是我不确定是否有更好的方法来执行它,例如使用代理,或通过Java互操作的手动线程,或者什么.我是并发编程的新手,所以不同方法的任何输入都会很棒.

  3. 从我所看到的,似乎Clojure的引用类型在并发编程中起着重要作用,我可以看到原因,但是在使用多个线程时是否总是需要使用它们?我是否应该担心我的一些数据是否可变?如果是这样,我正在编写的解析器的代码中应该是什么特别可变的?什么参考类型最适合我正在做的事情?我的程序工作方式的本质(用户以文本文件作为参数运行程序 - 程序处理它并将其转换为音频)使得看起来我不需要任何可变的东西,因为输入数据永远不会改变,所以我的直觉告诉我我不需要使用任何引用类型,但话说再说一遍,我可能不完全理解引用类型和Clojure中的并发性之间的关系.

Ale*_*x D 6

我建议您过早优化可能会让自己从更重要的事情(比如制定音乐语言的细节)中分散注意力.最好编写最简单,最容易编码的解析器,您可以先启动并运行它.如果您发现它太慢,那么您可以查看如何优化以获得更好的性能.

解析器应该是相当自包含的,并且可能无论如何都不会占用大量代码,因此即使您稍后将其丢弃并重写它,也不会是一个很大的损失.编写第一个解析器的经验将有助于编写第二个解析器.

其他要点:

你对引用类型绝对正确 - 你可能不需要任何引用类型.你的程序是一个编译器 - 它需要输入,转换它,写入输出,然后退出.这是纯函数式编程的理想情况,没有任何可变性,所有数据流纯粹通过函数参数和返回值.

使用解析器生成器通常是获得工作解析器的最快方法,但我还没有为Clojure找到一个非常好的解析器生成器.Parsley有一个非常好的API,但它会生成LR(0)解析器,对于每个"section"的开头/结尾都没有明确,明确的标记的任何东西几乎都没用.(就像S表达式用parens打开和关闭一样.)那里有几个解析器组合库,比如squarepeg,但是我不喜欢他们的API,而是喜欢编写我自己的手工编码,递归下降解析器我自己实现的解析器组合器.(它们并不快,但代码读得非常好.)