2008-10-21 5 views
25

Avec le code de lecture de fichier suivant:En C#, si 2 processus sont la lecture et l'écriture dans le même fichier, quelle est la meilleure façon d'éviter des exceptions de blocage du processus?

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None)) 
{ 
    using (TextReader tr = new StreamReader(fileStream)) 
    { 
     string fileContents = tr.ReadToEnd(); 
    } 
} 

Et le code d'écriture de fichier suivant:

using (TextWriter tw = new StreamWriter(fileName)) 
{ 
    tw.Write(fileContents); 
    tw.Close(); 
} 

Les détails d'exception suivants sont observés:

Le processus ne peut pas accéder au fichier « c: \ temp \ myfile.txt » parce qu'il est utilisé par un autre processus.

Quelle est la meilleure façon d'éviter cela? Le lecteur doit-il réessayer à la réception de l'exception ou existe-t-il un meilleur moyen?

Notez que le processus de lecteur utilise un FileSystemWatcher savoir quand le fichier a changé.

Notez également que, dans ce cas, je suis et non à la recherche d'autres moyens de partager les chaînes entre les deux processus.

Répondre

34

Vous pouvez ouvrir un fichier en écriture et verrouiller uniquement l'accès en écriture, ce qui permet à d'autres de lire le fichier.

Par exemple,

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read)) 
{ 
    // Do your writing here. 
} 

Autre accès aux fichiers ouvre simplement le fichier pour la lecture et ne pas écrire, et permet le partage readwrite.

using (FileStream stream = new FileStream(@"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
{ 
    // Does reading here. 
} 

Si vous voulez vous assurer que les lecteurs liront toujours un dossier à jour, vous devrez soit utiliser un fichier de verrouillage qui indique que quelqu'un est en train d'écrire dans le fichier (si vous pouvez obtenir une condition de course sinon soigneusement mis en œuvre) ou assurez-vous d'écriture de bloc de partage lors de l'ouverture à lire et à gérer l'exception afin que vous puissiez essayer de nouveau jusqu'à ce que vous obtenez un accès exclusif.

+0

Mais dans ce cas, si je comprends bien le fonctionnement, vous pouvez lire un fichier incomplet? –

+3

C'est vrai, oui. Cela sera toujours le cas si vous autorisez plus d'un consommateur à accéder au fichier.La seule façon d'y parvenir et de synchroniser consiste à utiliser un fichier de verrouillage ou à intercepter l'exception d'accès et à réessayer jusqu'à obtenir un accès exclusif. –

+0

Je suis un peu confus parce que cela semble contredire la réponse acceptée à cette question: http://stackoverflow.com/questions/25097773/system-io-filestream-fileaccess-vs-fileshare, car il stipule que pour FileAcces.ReadWrite vous devez utiliser FileShare.None. – user2481095

2

Vous pouvez utiliser un objet Mutex pour cela.

+0

Merci pour la réponse rapide. Que faire si les processus sont sur des systèmes différents et que le fichier est partagé sur le réseau? – Iain

+0

Hehe, ça ne marchera pas. Utilisez un fichier 'lock' de zéro octet comme le font les services Linux. – leppie

+0

Ou mieux encore, écrivez un service, et utilisez-le pour contrôler l'accès au fichier avec un Mutex, ceci devrait être étendu avec un exemple et un mutex global cause le réponse "populaire" à cette question en ce moment est un code vraiment risqué. –

6

Si vous créez un Mutex nommé, vous pouvez définir le mutex dans l'application d'écriture et attendre que le mutex soit libéré. Donc, dans le processus de notification qui fonctionne actuellement avec FileSystemWatcher, vérifiez simplement si vous avez besoin d'attendre le mutex, si vous le faites, il attendra, puis traitera.

Voici un VB example of a Mutex comme celui-ci que j'ai trouvé, il devrait être assez facile à convertir en C#.

2

Obtenez votre processus pour vérifier l'état du dossier si elle est en cours d'écriture. Vous pouvez le faire par la présence d'un fichier de verrouillage (à savoir la présence de cet autre fichier, qui peut être vide, empêche l'écriture du fichier principal). Cependant, même si ce n'est pas une sécurité, les deux processus peuvent créer le fichier de verrouillage en même temps, mais vous pouvez le vérifier avant de valider l'écriture.

Si votre processus rencontre un fichier de verrouillage puis l'obtenir pour dormir simplement/attendre et essayer à nouveau à un intervalle prédéfini dans l'avenir.

1

écrire dans un fichier temporaire, lorsque vous avez terminé l'écriture renommage/déplacer le fichier vers l'emplacement et/ou le nom que le lecteur cherche.

3

est-il une raison particulière pour ouvrir le fichier avec FileShare.None? Cela empêchera le fichier d'être ouvert par un autre processus. FileShare.Write ou FileShare.ReadWrite devrait permettre à l'autre processus (sous réserve de permissions) d'ouvrir et d'écrire dans le fichier pendant que vous le lisez, mais vous devrez surveiller le changement de fichier sous vous pendant que vous lisez il - simplement tamponner le contenu lors de l'ouverture peut aider ici. Toutes ces réponses, cependant, sont également valables - la meilleure solution dépend exactement de ce que vous essayez de faire avec le fichier: s'il est important de le lire tout en garantissant qu'il ne change pas, puis le verrouiller et gérer l'exception subséquente dans votre code d'écriture; S'il est important de lire et d'écrire en même temps, modifiez la constante FileShare.

2

Le lecteur et écrivain à la fois besoin d'une nouvelle tentative des mécanismes. Aussi FileShare doit être réglé sur FileShare.read pour les lecteurs et FileShare.None pour l'écrivain. Cela devrait garantir que les lecteurs ne lisent pas le fichier pendant l'écriture.

Le lecteur (hors nouvelle tentative) devient

using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    using (TextReader tr = new StreamReader(fileStream)) 
    { 
     string fileContents = tr.ReadToEnd(); 
    } 
} 

L'auteur (hors nouvelle tentative) devient:

FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None); 
using (TextWriter tw = new StreamWriter(fileStream)) 
{ 
    tw.Write(fileContents); 
    tw.Close(); 
} 
1

La meilleure chose à faire, est de mettre un protocole d'application sur le dessus d'un fichier transfert/mécanisme de transfert de propriété. Le mécanisme "lock-file" est un vieux hack UNIX qui existe depuis longtemps. La meilleure chose à faire est de simplement "remettre" le fichier au lecteur. Il y a plusieurs manières de faire ça. Vous pouvez créer le fichier avec un nom de fichier aléatoire, puis "donner" ce nom au lecteur. Cela permettrait à l'écrivain d'écrire de manière asynchrone un autre fichier. Pensez à la façon dont la «page Web» fonctionne. Une page Web a un "lien" vers plus d'informations, des images, des scripts, du contenu externe, etc. Le serveur vous donne cette page, parce que c'est une vue cohérente de la "ressource" que vous voulez. Votre navigateur va ensuite et obtient le contenu approprié, en fonction de ce que la description de la page (le fichier HTML ou autre contenu retourné), puis transfère ce dont il a besoin.

Ceci est le type le plus élastique du mécanisme de « partage » à utiliser. Ecrivez le fichier, partagez le nom, passez au fichier suivant. La partie "partage du nom" est le transfert atomique qui s'assure que les deux parties (le lecteur et l'auteur) sont d'accord sur le fait que le contenu est "complet".

Questions connexes