2008-10-21 4 views
25

Con el siguiente código de lectura de archivo:En C#, si 2 procesos están leyendo y escribiendo en el mismo archivo, ¿cuál es la mejor manera de evitar excepciones de bloqueo de procesos?

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

Y el siguiente código de escritura de archivo:

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

se observan los siguientes detalles de la excepción:

El proceso no tiene acceso al archivo 'c: \ temp \ myfile.txt' porque es que está siendo utilizado por otro proceso.

¿Cuál es la mejor manera de evitar esto? ¿El lector debe volver a intentar al recibir la excepción o hay alguna forma mejor?

Tenga en cuenta que el proceso del lector utiliza un FileSystemWatcher para saber cuándo ha cambiado el archivo.

También tenga en cuenta que, en este caso, soy no buscando alternativas para compartir cadenas entre los 2 procesos.

Respuesta

34

Puede abrir un archivo para escribir y solo bloquear el acceso de escritura, permitiendo así que otros sigan leyendo el archivo.

Por ejemplo,

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

Otro acceso a archivos simplemente abre el archivo para la lectura y la escritura no, y permite el intercambio de lectura-escritura.

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

Si desea asegurarse de que los lectores siempre van a leer un archivo de puesta al día, usted necesitará utilizar un archivo de bloqueo que indica que alguien está escribiendo en el archivo (aunque es posible obtener una condición de carrera si no se implementa con cuidado) o asegúrese de bloquear el uso compartido de escritura al abrir para leer y manejar la excepción, de modo que puede volver a intentarlo hasta obtener acceso exclusivo.

+0

Pero en este caso, si entiendo el funcionamiento correctamente, ¿podría leer un archivo incompleto? –

+3

Eso es cierto, sí. Ese siempre será el caso si permite que más de un consumidor acceda al archivo. La única manera de lograr esto y sincronizar es usar un archivo de bloqueo o atrapar la excepción de acceso e intentar nuevamente hasta que obtenga acceso exclusivo. –

+0

Estoy un poco confundido porque esto parece contradecir la respuesta aceptada a esta pregunta: http://stackoverflow.com/questions/25097773/system-io-filestream-fileaccess-vs-fileshare porque indica que para FileAcces.ReadWrite debe usar FileShare.None. – user2481095

2

Puede usar un objeto Mutex para esto.

+0

Gracias por la respuesta rápida. ¿Qué ocurre si los procesos están en diferentes sistemas y el archivo se está compartiendo a través de la red? – Iain

+0

Jeje, eso no funcionará entonces. Use un archivo de bloqueo de cero byte como los servicios de Linux :) – leppie

+0

O mejor aún escriba un servicio y utilícelo para controlar el acceso al archivo con un Mutex. Esto se debe expandir con una muestra y un mutex global que provoque la La respuesta "popular" a esta pregunta en este momento es un código realmente arriesgado. –

6

Si crea un Mutex con nombre, puede definir el mutex en la aplicación de escritura y hacer que la aplicación de lectura espere hasta que se libere el mutex.

Por lo tanto, en el proceso de notificación que está trabajando actualmente con FileSystemWatcher, simplemente verifique si necesita esperar el mutex; si lo hace, esperará y luego procesará.

Aquí hay un VB example of a Mutex como este que encontré, debería ser lo suficientemente fácil de convertir a C#.

2

Obtenga su proceso para verificar el estado del archivo si se está escribiendo. Puede hacerlo mediante la presencia de un archivo de bloqueo (es decir, la presencia de este otro archivo, que puede estar vacío, evita que se escriba en el archivo principal).

Incluso esto no es a prueba de fallos, ya que los dos procesos pueden crear el archivo de bloqueo al mismo tiempo, pero puede verificar esto antes de confirmar la escritura.

Si su proceso encuentra un archivo de bloqueo, póngalo simplemente en modo reposo/espera y vuelva a intentarlo en un intervalo predefinido en el futuro.

1

Escribir en un archivo temporal, cuando termine de escribir renombre/mueva el archivo a la ubicación y/o el nombre que el lector está buscando.

3

¿Hay alguna razón en particular para abrir el archivo con FileShare.None? Eso evitará que otro proceso abra el archivo.

FileShare.Write o FileShare.ReadWrite debe permitir que el otro proceso (sujeto a permisos) se abra y escriba en el archivo mientras lo está leyendo, sin embargo, deberá observar el cambio del archivo debajo de usted mientras lee esto - simplemente almacenar el contenido al abrir puede ayudar aquí.

Todas estas respuestas, sin embargo, son igualmente válidas: la mejor solución depende exactamente de lo que intente hacer con el archivo: si es importante leerlo mientras se garantiza que no cambia, bloqueelo y manejar la excepción posterior en su código de escritura; si es importante leer y escribir al mismo tiempo, entonces cambie la constante de FileShare.

2

Tanto el lector como el escritor necesitan mecanismos de reintento. También FileShare debe establecerse en FileShare.read para los lectores y FileShare.none para el escritor. Esto debería asegurar que los lectores no lean el archivo mientras la escritura está en progreso.

El lector (con exclusión de reintento) se convierte en

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

El escritor (con exclusión de reintento) se convierte en:

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

Lo mejor que puede hacer, es poner un protocolo de aplicación en la parte superior de un archivo mecanismo de transferencia/transferencia de propiedad. El mecanismo de "bloqueo de archivo" es un viejo truco de UNIX que ha existido por siglos. Lo mejor que puede hacer es simplemente "entregarle" el archivo al lector. Hay muchas formas de hacer esto. Puede crear el archivo con un nombre de archivo aleatorio y luego "dar" ese nombre al lector. Eso le permitiría al escritor escribir de manera asíncrona otro archivo. Piensa en cómo funciona la "página web". Una página web tiene un "enlace" a más información, imágenes, scripts, contenido externo, etc. El servidor le entrega esa página, porque es una vista coherente del "recurso" que desea. Luego, su navegador se activa y obtiene el contenido apropiado, según la descripción de la página (el archivo HTML u otro contenido devuelto), y luego transfiere lo que necesita.

Este es el tipo más flexible de mecanismo de "uso compartido" para usar. Escribe el archivo, comparte el nombre, pasa al siguiente archivo. La parte "compartir el nombre" es la mano atómica que asegura que ambas partes (el lector y el escritor) estén de acuerdo en que el contenido es "completo".

Cuestiones relacionadas