2011-06-19 2 views
7

J'ai un groupe de tâches de traitement des futures à partir d'une file d'attente qui implique l'écriture dans des fichiers. Quelle est la manière idiomatique de s'assurer qu'un seul futur accède à un fichier particulier à la fois?verrouillage idiomatique de fichier dans clojure?

Répondre

8

Que diriez-vous d'utiliser des agents au lieu de verrous pour vous assurer cela? Je pense que l'utilisation d'agents pour sauvegarder l'état mutable partagé, que ce soit en mémoire ou sur le disque, est plus idiomatique dans la clojure que l'utilisation de verrous.

Si vous créez un agent à la fois et que vous envoyez les tentatives d'accès aux agents, vous pouvez vous assurer que seul le thread à l'heure accède à un fichier donné.

Par exemple comme ceci:

(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))) 

puis ajoutez votre dossier par l'agent:

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

Si vous avez besoin d'utilisation synchrone du fichier, il pourrait être atteint avec vous attendent. Comme ceci:

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

Neat! Il m'a fallu un peu de temps pour comprendre cela. J'avais fini par utiliser une référence à une carte contenant une entrée pour chaque fichier ouvert. Votre solution semble être beaucoup plus agréable. – jhickner

+0

Je ne suis toujours pas clair sur quelque chose - si deux threads appellent async-append et transmettent le même nom de fichier, comment les empêcher d'ouvrir le fichier? Il semble que l'appel à l'agent de fichier dans async-append créerait un agent unique pour chaque thread, non? – jhickner

+0

Désolé pour les instructions peu claires, je voulais créer un agent de fichier par fichier et l'ajouter soit par async-append ou sync-append selon le besoin de l'ajout asynchrone ou synchrone. Vous pouvez garder les agents eux-mêmes dans une carte pour trouver l'agent correct. –

1

Je ne pense pas qu'il existe une fonction intégrée spécifique pour Clojure, mais vous pouvez utiliser les fonctions d'E/S java standard pour ce faire. Cela ressemblerait à quelque chose comme ceci:

(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

J'ai d'abord vérifié cette avenue, mais malheureusement, cette méthode n'est utile que pour verrouiller le fichier à partir d'autres processus. Lorsque vous travaillez avec plusieurs threads dans la même machine virtuelle, lock et tryLock lancent une exception si un autre thread de la même machine virtuelle a déjà le fichier verrouillé, plutôt que de bloquer jusqu'à ce que le fichier soit disponible. – jhickner

4

J'utiliser la fonction essentielle Clojure locking qui est utilisé comme suit:

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

Ici some-object pourrait être soit le fichier lui-même, ou encore tout objet arbitraire que vous souhaitez synchroniser sur (ce qui pourrait avoir du sens si vous vouliez un seul verrou pour protéger plusieurs fichiers).

Sous le capot, cela utilise le verrouillage d'objet JVM standard, donc il est fondamentalement équivalent à un bloc de code synchronisé en Java.

Questions connexes