8
votes

Comment verrouiller un fichier et éviter les lectures pendant qu'il écrit

Mon application Web renvoie un fichier à partir du système de fichiers. Ces fichiers sont dynamiques, donc je n'ai aucun moyen de connaître les noms o combien d'entre eux y aura-t-il. Lorsque ce fichier n'existe pas, l'application la crée à partir de la base de données. Je veux éviter que deux threads différents recréent le même fichier simultanément ou qu'un thread essaie de renvoyer le fichier pendant que d'autres threads le créent.

En outre, je ne veux pas obtenir de verrouillage sur un élément commun pour tous les fichiers. Par conséquent, je devrais verrouiller le fichier juste lorsque je le crée.

Donc, je veux verrouiller un fichier jusqu'à ce que sa récréation soit complète, si un autre thread essaie d'y accéder ... il devra attendre le fichier être déverrouillé.

J'ai lu sur filestream.lock, mais je dois connaître la longueur de fichier et cela n'empêchera pas que l'autre thread essaie de lire le fichier, de sorte que cela ne fonctionne pas pour mon cas particulier.

Je lis aussi à propos de FileShare.none, mais il lancera une exception (quel type d'exception?) Si autre thread / processus tente d'accéder au fichier ... donc je devrait développer une "réessaye à nouveau tout en fauchant" car j'aimerais éviter la génération d'exception ... et je n'aime pas trop cette approche, bien que peut-être qu'il n'y ait pas une meilleure façon.

L'approche avec fichier à fileshare.none serait plus ou moins: xxx

mais je n'aime pas le fait que je dois attraper des exceptions, essayez plusieurs fois et attendez une impasse quantité de temps: |


3 commentaires

C'est FileShare.none au lieu de FileAccess.none (FileAccess définit l'accès à votre application, tandis que FileShare est utilisé pour verrouiller le fichier, comme vous le souhaitez)


Sur un autre sujet, n'oubliez pas de déverrouiller le fichier chaque fois que votre application est détruite, je déteste lorsque le verrouillage reste après.


J'ai édité et résolu, merci!


6 Réponses :


1
votes

Je pense que l'une plus droite serait la suivante: Créer un ensemble de chaîne avez-vous enregistré le nom du fichier actuel Donc, un fil traiterait le fichier à l'heure, quelque chose comme celui-ci xxx


2 commentaires

C'est le problème que je ne veux pas verrouiller un élément commun pour tous les fichiers. Tout d'abord, obtenez une serrure coûte cher et je ne veux pas obtenir un verrou pour chaque appel en demandant un fichier, quel que soit le fichier existant ou non. Deuxièmement, je ne veux pas bloquer les threads qui essaient d'obtenir un fichier différent, car j'en crée l'un d'eux. Pour ces raisons, je veux verrouiller le fichier lui-même. À votre santé.


Avez-vous mesuré le temps d'acquérir une serrure et un blocage jusqu'à l'achèvement et de l'époque pour le fil qui vous réveille, vérifiant l'accès, obtenez une exception, dormir et répéter plusieurs fois? Je m'attends à ce qu'une stratégie de verrouillage soit plus souhaitable ici. thread.sleep est moins souhaitable de bloquer sur une serrure. Et si le fil d'écriture se termine tôt? Le fil de lecture ne se réveille pas. Vous voudrez peut-être envisager un manuelResetEvent pour contrôler l'accès entre les deux threads.



3
votes

Vous pouvez gérer cela en utilisant l'argument FileMode.creenew au constructeur de flux. L'un des threads va perdre et découvrir que le fichier a déjà été créé un microseconde plus tôt par un autre fil. Et obtiendra une ioexception.

Il aura alors besoin de tourner, attendre que le fichier soit entièrement créé. Que vous appliquez avec le fichier à filé.none. Attraper des exceptions ici n'a pas d'importance, il tourne quand même. Il n'y a pas d'autre solution de contournement de toute façon, sauf si vous êtes p / invoke.


1 commentaires

Il semble que vous ayez raison, il n'y a pas d'autre solution de contournement pour cela. Merci!



1
votes

Avez-vous un moyen d'identifier les fichiers créés?

Dites que chacun de ces fichiers correspond à un identifiant unique dans votre base de données. Vous créez un emplacement centralisé (singleton?), Où ces identifiants peuvent être associés à quelque chose de verrouillable (dictionnaire). Un fil qui doit lire / écrire sur l'un de ces fichiers effectue les suivants: xxx

bien sûr, les threads qui ne suivent pas ce protocole de verrouillage exact auront accès à la Fichier.

Maintenant, le verrouillage d'un objet singleton n'est certainement pas idéal, mais si votre application nécessite une synchronisation globale, c'est un moyen de le réaliser.


3 commentaires

Même problème que le code @hworangdo, dans chaque demande, vous devez acquérir une serrure, même lorsque vous n'en avez pas besoin.


@vtortola: Oui. En défense de ma réponse: Obtenir une serrure n'est pas chère (mesurez-la, c'est vraiment rien, notamment par rapport au fichier IO), mais en attente d'un autre thread de libérer le verrouillage. Vous pouvez essayer de trouver une implémentation du dictionnaire sans verrouillage. Vous n'avez besoin que de faire attention dans le cas où le fichier doit être créé, de sorte qu'un seul fil devienne chargé de la créer.


Vous avez probablement raison, je n'ai jamais testé à quel point l'expression adquente est-elle moi-même, je le sais parce que je l'ai lu. En attente d'un autre thread de libérer le verrou est plus cher, mais cela ne se produira qu'une fois par fichier. Je vais tester votre approche plus tard, peut-être que le vôtre est plus rapide. Merci!



1
votes

Votre question m'a vraiment fait penser.

Au lieu d'avoir chaque fil responsable de l'accès des fichiers et de les avoir bloqués, que si vous utilisez une file d'attente de fichiers à persister et que vous avez une seule filière de travail de fond de travail et persistez?

tandis que le travailleur d'arrière-plan est en train de démarrer, vous pouvez afficher les threads d'application Web renvoyer les valeurs de base de données jusqu'à ce que le fichier existe réellement.

J'ai posté une très simple Exemple de cela sur GitHub .

N'hésitez pas à lui donner un coup et laissez-moi savoir ce que vous pensez.

FYI, si vous n'avez pas de git, vous pouvez utiliser svn pour le tirer http://svn.github.com/statianzo/multhreadfileAccesswebApp


0 commentaires

0
votes

Pourquoi n'utilisez-vous pas simplement la base de données - E.G. Si vous avez un moyen d'associer un nom de fichier avec les données de la DB Il contient, ajoutez simplement des informations à la DB qui spécifie si un fichier existe avec ces informations actuellement et lorsqu'il a été créé, à quel point les informations contaminées dans le fichier sont. . Lorsqu'un fil a besoin d'informations, il vérifie la DB pour voir si ce fichier existe et sinon, il écrit une rangée à la table indiquant qu'il crée le fichier. Quand il est fait, il met à jour cette ligne avec un dicton booléen, le fichier est prêt à être utilisé par d'autres.

La bonne chose à propos de cette approche - Toutes vos informations sont en 1 place - afin que vous puissiez faire une belle récupération d'erreur - par exemple. Si le fil créant le fichier meurt mal pour une raison quelconque, un autre thread peut venir et décider de réécrire le fichier car le temps de création est trop vieux. Vous pouvez également créer des processus de nettoyage de lots simples et obtenir des données précises sur la fréquence à laquelle certaines données sont utilisées pour un fichier, quelle fréquence les informations sont mises à jour (en regardant les temps de création, etc.). En outre, vous évitez de devoir avoir à faire de nombreux disques sur votre système de fichiers sous forme de threads différents, recherchez différents fichiers sur le lieu - surtout si vous décidez d'avoir plusieurs machines frontales qui recherchent sur un disque commun.

La chose délicate - vous devrez vous assurer que votre dB prend en charge le verrouillage du niveau de ligne sur la table qui threads écrit sur lorsqu'ils créent des fichiers car, sinon, la table elle-même peut être verrouillée qui pourrait rendre cela inacceptable lent.


0 commentaires

1
votes

La question est ancienne et il y a déjà une réponse marquée. Néanmoins, je voudrais poster une alternative plus simple.

Je pense que nous pouvons utiliser directement l'instruction de verrouillage sur le nom de fichier, comme suit: xxx

généralement, verrouillage d'une chaîne est une mauvaise idée en raison de la chaîne interne. Mais dans ce cas particulier, il devrait s'assurer que personne d'autre ne peut accéder à cette serrure. Il suffit d'utiliser la même chaîne de verrouillage avant de tenter de lire. Ici, interne travaille pour nous et non contre.

PS: le texte 'Filelock' est juste un texte arbitraire pour vous assurer que d'autres chemins de fichiers de chaîne ne sont pas affectés.


0 commentaires