En C#, si 2 procesos están leyendo y escribiendo en el mismo archivo, ¿cuál es la mejor manera de evitar las excepciones de bloqueo de procesos?


Con el siguiente código de lectura de archivo:

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 archivo escribe código:

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

Se ven los siguientes detalles de excepción:

El proceso no puede acceder al archivo 'c:\temp\myfile.txt', porque es ser utilizado por otro proceso.

¿Cuál es la mejor manera de evitar esto? ¿Necesita el lector volver a intentarlo al recibir la excepción o hay alguna manera mejor?

Tenga en cuenta que el proceso de lectura está utilizando un FileSystemWatcher para saber cuándo ha cambiado el archivo.

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

Author: Jeff Yates, 2008-10-21

8 answers

Puede abrir un archivo para escritura y solo bloquear el acceso de escritura, permitiendo así que otros aún lean 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 solo abre el archivo para leer y no escribir, y permite compartir readwrite.

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 lean un archivo actualizado, deberá usar un archivo de bloqueo que indique que alguien está escribiendo en el archivo (aunque puede obtener una condición de carrera si no con cuidado implementado) o asegúrese de bloquear el uso compartido de escritura al abrir para leer y manejar la excepción para que pueda intentarlo de nuevo hasta que obtenga acceso exclusivo.

 34
Author: Jeff Yates,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 14:58:33

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.

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

Aquí hay un VB ejemplo de un Mutex como este que encontré, debería ser bastante fácil convertir a C#.

 6
Author: Mitchel Sellers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 14:37:14

Hay alguna razón en particular para abrir el archivo con FileShare.¿Ninguna? Eso evitará que el archivo sea abierto por cualquier otro proceso.

Archivo compartido.Escribir o compartir archivos.ReadWrite debería permitir que el otro proceso (sujeto a permisos) abra y escriba en el archivo mientras lo está leyendo, sin embargo, tendrá que estar atento a que el archivo cambie debajo de usted mientras lo lee; simplemente almacenar en búfer el contenido al abrirlo puede ayudar aquí.

Todas estas respuestas, sin embargo, son igualmente válida - la mejor solución depende exactamente de lo que esté tratando de hacer con el archivo: si es importante leerlo mientras garantiza que no cambie, entonces bloquéelo y maneje la excepción posterior en su código de escritura; si es importante leerlo y escribirlo al mismo tiempo, entonces cambie la constante de archivo compartido.

 3
Author: symonc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 14:51:10

Puede usar un objeto Mutex para esto.

 2
Author: leppie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 14:32:04

Obtenga su proceso para verificar el estado del archivo si está siendo escrito. Puede hacer esto por la presencia de un archivo de bloqueo (es decir, la presencia de este otro archivo, que puede estar vacío, impide escribir en el archivo principal).

Sin embargo, 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, haga que simplemente duerma / espere y vuelva a intentarlo intervalo predefinido en el futuro.

 2
Author: Anthony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 14:38:05

Tanto el lector como el escritor necesitan mecanismos de reintento. También FileSharedebe establecerse como 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 (excluyendo 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 (excluyendo 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();
}
 2
Author: Iain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 15:02:26

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

 1
Author: KristoferA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-21 14:44:29

Lo mejor es poner un protocolo de aplicación encima de un mecanismo de transferencia de archivos/transferencia de propiedad. El mecanismo "lock-file" es un viejo hack de UNIX que ha existido durante siglos. Lo mejor que puede hacer, es simplemente "entregar" el archivo al lector. Hay muchas maneras de hacer esto. Puede crear el archivo con un nombre de archivo aleatorio y luego " dar " ese nombre al lector. Eso permitiría al escritor escribir asíncronamente otro archivo. Piensa en cómo funciona la "página web". Un la página web tiene un "enlace"a más información en ella, para imágenes, scripts, contenido externo, etc. El servidor le entrega esa página, porque es una vista coherente del" recurso " que desea. Su navegador entonces va y obtiene el contenido apropiado, basado en lo que 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 resistente de mecanismo de "compartir" a utilizar. Escribe el archivo, comparte el nombre, pasa al siguiente archivo. El " compartir el nombre " parte, es la mano atómica que se asegura de que ambas partes (el lector y el escritor) están de acuerdo en que el contenido es "completo."

 1
Author: grwww,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-07 20:21:12