Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 确保Clojure中只有一个服务实例正在运行/启动/停止的规范方法?_Multithreading_Clojure_Parallel Processing_Singleton_Stm - Fatal编程技术网

Multithreading 确保Clojure中只有一个服务实例正在运行/启动/停止的规范方法?

Multithreading 确保Clojure中只有一个服务实例正在运行/启动/停止的规范方法?,multithreading,clojure,parallel-processing,singleton,stm,Multithreading,Clojure,Parallel Processing,Singleton,Stm,我正在用Clojure编写一个由Neo4j支持的有状态服务器,它可以服务套接字请求,比如HTTP。当然,这意味着我需要能够从这个服务器中启动和停止套接字服务器。在设计方面,我希望能够在这个服务器中声明一个“服务”,并启动和停止它 在Clojure中,我想强调的是如何确保启动和停止这些服务是线程安全的。我正在编写的这个服务器将嵌入NREPL,并以并行方式处理传入的请求。其中一些请求将是管理性的:启动服务X,停止服务Y。这就增加了两个启动请求同时进入的可能性 启动应同步检查“运行”标志和“启动”标志

我正在用Clojure编写一个由Neo4j支持的有状态服务器,它可以服务套接字请求,比如HTTP。当然,这意味着我需要能够从这个服务器中启动和停止套接字服务器。在设计方面,我希望能够在这个服务器中声明一个“服务”,并启动和停止它

在Clojure中,我想强调的是如何确保启动和停止这些服务是线程安全的。我正在编写的这个服务器将嵌入NREPL,并以并行方式处理传入的请求。其中一些请求将是管理性的:启动服务X,停止服务Y。这就增加了两个启动请求同时进入的可能性

  • 启动应同步检查“运行”标志和“启动”标志,如果设置了其中一个标志,则启动失败。在同一事务中,应设置“开始”标志
  • 设置“开始”标志后,事务关闭。这使得“开始”标志对其他事务可见
  • 然后,(启动)函数实际启动服务
  • 如果(启动)成功,则同步设置“运行”和“启动”标志
  • 如果(启动)失败,则设置“启动”标志并返回异常
  • 停止需要同样的东西,检查“运行”标志,检查并设置它自己的“停止”标志

    我试图通过(开始)和(停止)的所有可能组合进行推理

    我错过什么了吗

    这已经有图书馆了吗?如果不是,像这样的库应该是什么样子?我将把它开源并放到Github上

    编辑:

    这就是我目前所拥有的。但我能看到一个洞。我错过了什么

    (ns extenium.db
      (:require [clojure.tools.logging :as log])
      (:import org.neo4j.graphdb.factory.GraphDatabaseFactory))
    
    (def ^:private
      db- (ref {:ref nil
                :running false
                :starting false
                :stopping false}))
    
    (defn stop []
      (dosync
       (if (or (not (:running (ensure db-)))
               (:stopping (ensure db-)))
         (throw (IllegalStateException. "Database already stopped or stopping."))
         (alter db- assoc :stopping true)))
      (try
        (log/info "Stopping database")
        (.shutdown (:ref db-))
        (dosync
         (alter db- assoc :ref nil))
        (log/info "Stopped database")
        (finally
          (dosync
           (alter db- assoc :stopping false)))))
    
    在try块中,我记录,然后调用。关机,然后再次记录。如果第一个日志失败(可能发生I/O异常),则(:stopping db-)将设置为false,这将取消阻止它,并且是正常的。shutdown是Neo4j中的一个void函数,所以我不必计算返回值。如果失败,(:stopping db-)设置为false,也可以。然后我将(:ref db-)设置为nil。如果失败了怎么办?(:stopping db-)设置为false,但(:ref db-)保持挂起状态。那是个洞。与第二次日志调用的情况相同。我错过了什么

    (ns extenium.db
      (:require [clojure.tools.logging :as log])
      (:import org.neo4j.graphdb.factory.GraphDatabaseFactory))
    
    (def ^:private
      db- (ref {:ref nil
                :running false
                :starting false
                :stopping false}))
    
    (defn stop []
      (dosync
       (if (or (not (:running (ensure db-)))
               (:stopping (ensure db-)))
         (throw (IllegalStateException. "Database already stopped or stopping."))
         (alter db- assoc :stopping true)))
      (try
        (log/info "Stopping database")
        (.shutdown (:ref db-))
        (dosync
         (alter db- assoc :ref nil))
        (log/info "Stopped database")
        (finally
          (dosync
           (alter db- assoc :stopping false)))))
    

    如果我只是使用Clojure的锁定原语而不是ref-dance,这会更好吗?

    这实际上是一个简单锁定的自然选择:

    (locking x
      (do-stuff))
    
    此处
    x
    是要同步的对象

    详细说明:启动和停止服务是一种副作用;不应从事务内部启动副作用,除非可能作为代理操作。这里的锁正是设计所需要的。请注意,在Clojure中使用它们并没有什么错,因为它们非常适合当前的问题,事实上我想说,
    锁定
    是这里的标准解决方案。(参见Stuart Halloway的《Clojure编程》(第1版)中介绍的Clojure库使用锁的示例,该锁在莱宁根得到了广泛的应用。)

    更新:添加快速故障行为:

    这仍然非常适合锁,即(Javadoc的follow链接):


    如果锁获取成功,将执行
    (do stuff)
    ;否则,
    (做其他事情)
    就会发生。当前线程在这两种情况下都不会阻塞。

    这听起来像是代理的一个很好的用例,它们允许您将更改序列化到一个可变状态,具有良好的概述。 您可以使用错误处理程序和代理错误方法来处理异常,而无需担心锁或争用条件

    (def service (agent {:status :stopped}))
    
    (defn start-service [{:keys [status] :as curr}]
      (if (= :stopped status)
        (do 
          (println "starting service")
          {:status :started})
        (do 
          (println "service already running")
          curr)))
    
     ;; start the service like this
     (send-off service start-service)
    
     ;; gets the current status of the service
     @service
    

    我在遵循runonce函数的逻辑方面有点困难。为什么把哨兵包裹在一个原子里?另外,我可以在同一个对象上同步start和stop,但是同时调用start或stop会阻塞。我希望他们尽快失败。有这样的模式吗?我已经修改了答案以考虑到这一点。至于Lancet的sentinel,其想法是您只希望包装在
    runonce
    中的函数在JVM的整个生命周期中运行一次,而不是“在任何给定时刻一次”/“一次只在一个线程中运行一次”,因此(1)您可以通过在sentinel上同步来防止同时执行,(2)通过检查前一次调用是否替换了atom中的值,可以防止在第一次调用之后再调用该函数。为(2)重用sentinel是很自然的,但与(1)中的角色无关。