2011-06-19 9 views
7

Tengo un grupo de trabajos de procesamiento de futuros desde una cola que implica escribir en archivos. ¿Cuál es la manera idiomática de asegurarse de que solo un futuro acceda a un archivo en particular a la vez?bloqueo de archivo idiomático en clojure?

Respuesta

8

¿Qué le parece usar agentes en lugar de cerraduras para asegurar esto?

Creo que usar agentes para proteger el estado mutable compartido, independientemente de si está en la memoria o en el disco, es más idiomático en clojure que usando bloqueos.

Si crea un agente a la vez y envía los intentos de acceso a los agentes, puede asegurarse de que solo en el hilo en el momento se accede a un archivo determinado.

Por ejemplo así:

(use 'clojure.contrib.duck-streams) 

(defn file-agent [file-name] 
    (add-watch (agent nil) :file-writer 
    (fn [key agent old new] 
     (append-spit file-name new)))) 

(defn async-append [file-agent content] 
    (send file-agent (constantly content))) 

continuación, anexar su archivo a través del agente:

(async-append "content written to file" (file-agent "temp-file-name")) 

Si necesita el uso sincrónica del archivo que se podría lograr con esperan. De esta manera:

(defn sync-append [file-agent content] 
    (await (send file-agent (constantly content)))) 
+0

¡Aseado! Tardé un poco en entender esto. Terminé usando una referencia a un mapa que contenía una entrada para cada archivo abierto. Su solución parece que podría ser mucho mejor. – jhickner

+0

Todavía no tengo claro algo: si dos subprocesos llaman async-append y pasan el mismo nombre de archivo, ¿qué evitará que ambos abran el archivo? Parece que la llamada a file-agent dentro de async-append crearía un agente único para cada hilo, ¿no? – jhickner

+0

Disculpe las instrucciones poco claras, quise crear un agente de archivos por archivo y luego anexarlo a través de async-append o sync-append dependiendo de lo que necesite anexar asíncrono o sincrónico. Puede mantener a los agentes ellos mismos tal vez en un mapa para encontrar el agente correcto. –

1

No creo que haya una función incorporada específica para esto en Clojure, pero puede usar las funciones estándar de IO de Java para hacerlo. Esto sería algo como esto:

(import '(java.io File RandomAccessFile)) 

(def f (File. "/tmp/lock.file")) 
(def channel (.getChannel (RandomAccessFile. f "rw"))) 
(def lock (.lock channel)) 
(.release lock) 
(.close channel) 
+0

Revisé esta avenida primero, pero desafortunadamente este método solo es útil para bloquear el acceso del archivo por otros procesos. Al trabajar con varios subprocesos en la misma máquina virtual, lock y tryLock arrojarán una excepción si otro subproceso en la misma máquina virtual ya tiene el archivo bloqueado, en lugar de bloquear hasta que el archivo esté disponible. – jhickner

4

me gustaría utilizar el núcleo función Clojure locking que se utiliza de la siguiente manera:

(locking some-object 
    (do-whatever-you-like)) 

Aquí some-object bien podría ser el propio archivo, o, alternativamente, cualquier objeto arbitrario que desea sincronizar encendido (lo que podría tener sentido si quisiera un solo candado para proteger varios archivos).

Debajo del capó esto utiliza el bloqueo de objetos JVM estándar, por lo que es básicamente equivalente a un bloque de código sincronizado en Java.

Cuestiones relacionadas