2010-09-27 8 views
6

Necesito abrir un archivo, pero si no está disponible en este momento, necesito esperar hasta que esté listo. ¿Cuál es el mejor enfoque para tomar?¿Cuál es el patrón correcto para esperar a que se libere un bloqueo de archivos?

ESCENARIO

estoy usando archivos como un mecanismo de caché persistente de datos de la aplicación. Esta información necesita ser leída y deserializada a menudo (escrita solo una vez, y borrada ocasionalmente). Tengo un proceso de limpieza que se ejecuta en un hilo separado que determina qué archivos ya no son necesarios y los elimina. La apertura y la lectura de archivos pueden ocurrir simultáneamente (raramente, pero podría suceder) y quiero que el proceso espere e intente leer los datos nuevamente.

Gracias!

+0

Es esto todo lo que tiene lugar dentro de un mismo proceso? –

+0

sí. En realidad, se está utilizando en un teléfono con Windows 7 (Silverlight). – Micah

Respuesta

0

Como todas las preguntas sobre "cuál es el mejor enfoque", esta depende de sus necesidades. Algunas opciones que vienen fácilmente a la mente:

  1. abandonar el intento de
  2. bucle hasta que el archivo se desbloquea
  3. preguntar al usuario qué hacer al respecto

Cuál ha elegido depende de cómo puedes lidiar con eso

+1

1) no es un medio muy efectivo de esperar que el bloqueo esté disponible. 3) no es una solución programática, aunque quizás la correcta. Y 2) es, sospecho, la solución predeterminada sobre la cual Micah intenta mejorar. –

+0

Me gustaría señalar que la pregunta original no se parecía en nada a su forma actual. – Tergiver

2

Depende de quién controla el archivo. Si una parte de su aplicación necesita esperar hasta que otra parte de la aplicación termine de preparar el archivo, puede usar un ManualResetEvent. Es decir, en el inicio, el programa crea un nuevo evento:

public ManualResetEvent FileEvent = new ManualResetEvent(false);

Ahora, la parte del programa que está esperando a que el archivo tiene este código:

FileEvent.WaitOne();

Y la parte del programa que está creando el archivo hace esto cuando el archivo de lista:

FileEvent.Set();

Si su aplicación tiene que esperar un archivo que está siendo utilizado por otra aplicación sobre la que no tiene control, su única solución real es intentar continuamente abrir el archivo.

FileStream f = null; 
while (f == null) 
{ 
    try 
    { 
     f = new FileStream(...); 
    } 
    catch (IOException) 
    { 
     // wait a bit and try again 
     Thread.Sleep(5000); 
    } 
} 

Por supuesto, es probable que no quieren ponerse incondicionalmente IOException. Es probable que desee capturar las excepciones específicas que sabe cómo manejar (por ejemplo, no desea intentar de nuevo si tiene un DirectoryNotFoundException). Las funciones de E/S documentan qué excepciones se espera que generen y bajo qué circunstancias.

9

No soy un gran fan de la try/catch IOException porque:

  1. La razón de la excepción es desconocido.
  2. No me gustan las excepciones 'esperadas' ya que a menudo funciono con break on excpetion.

Puede hacer esto sin excepciones llamando CreateFile y devolver una corriente cuando/si finalmente devuelve un identificador:

public static System.IO.Stream WaitForExclusiveFileAccess(string filePath, int timeout) 
{ 
    IntPtr fHandle; 
    int errorCode; 
    DateTime start = DateTime.Now; 

    while(true) 
    { 
     fHandle = CreateFile(filePath, EFileAccess.GenericRead | EFileAccess.GenericWrite, EFileShare.None, IntPtr.Zero, 
          ECreationDisposition.OpenExisting, EFileAttributes.Normal, IntPtr.Zero); 

     if (fHandle != IntPtr.Zero && fHandle.ToInt64() != -1L) 
      return new System.IO.FileStream(fHandle, System.IO.FileAccess.ReadWrite, true); 

     errorCode = Marshal.GetLastWin32Error(); 

     if (errorCode != ERROR_SHARING_VIOLATION) 
      break; 
     if (timeout >= 0 && (DateTime.Now - start).TotalMilliseconds > timeout) 
      break; 
     System.Threading.Thread.Sleep(100); 
    } 


    throw new System.IO.IOException(new System.ComponentModel.Win32Exception(errorCode).Message, errorCode); 
} 

#region Win32 
const int ERROR_SHARING_VIOLATION = 32; 

[Flags] 
enum EFileAccess : uint 
{ 
    GenericRead = 0x80000000, 
    GenericWrite = 0x40000000 
} 

[Flags] 
enum EFileShare : uint 
{ 
    None = 0x00000000, 
} 

enum ECreationDisposition : uint 
{ 
    OpenExisting = 3, 
} 

[Flags] 
enum EFileAttributes : uint 
{ 
    Normal = 0x00000080, 
} 

[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode)] 
static extern IntPtr CreateFile(
    string lpFileName, 
    EFileAccess dwDesiredAccess, 
    EFileShare dwShareMode, 
    IntPtr lpSecurityAttributes, 
    ECreationDisposition dwCreationDisposition, 
    EFileAttributes dwFlagsAndAttributes, 
    IntPtr hTemplateFile); 

#endregion 
2

La versión más genérico del método del csharptest.net podría tener este aspecto (también, se usa SafeFileHandle y eliminado de lanzar una excepción en tiempo de espera, puede obtener valores de enumeración en http://www.pinvoke.net/default.aspx/kernel32.createfile):

public static FileStream WaitForFileAccess(string filePath, FileMode fileMode, FileAccess access, FileShare share, TimeSpan timeout) 
    { 
     int errorCode; 
     DateTime start = DateTime.Now; 

     while (true) 
     { 
      SafeFileHandle fileHandle = CreateFile(filePath, ConvertFileAccess(access), ConvertFileShare(share), IntPtr.Zero, 
                ConvertFileMode(fileMode), EFileAttributes.Normal, IntPtr.Zero); 

      if (!fileHandle.IsInvalid) 
      { 
       return new FileStream(fileHandle, access); 
      } 

      errorCode = Marshal.GetLastWin32Error(); 

      if (errorCode != ERROR_SHARING_VIOLATION) 
      { 
       break; 
      } 

      if ((DateTime.Now - start) > timeout) 
      { 
       return null; // timeout isn't an exception 
      } 

      Thread.Sleep(100); 
     } 

     throw new IOException(new Win32Exception(errorCode).Message, errorCode); 
    } 

    private static EFileAccess ConvertFileAccess(FileAccess access) 
    { 
     return access == FileAccess.ReadWrite ? EFileAccess.GenericRead | EFileAccess.GenericWrite : access == FileAccess.Read ? EFileAccess.GenericRead : EFileAccess.GenericWrite; 
    } 

    private static EFileShare ConvertFileShare(FileShare share) 
    { 
     return (EFileShare) ((uint) share); 
    } 

    private static ECreationDisposition ConvertFileMode(FileMode mode) 
    { 
     return mode == FileMode.Open ? ECreationDisposition.OpenExisting : mode == FileMode.OpenOrCreate ? ECreationDisposition.OpenAlways : (ECreationDisposition) (uint) mode; 
    } 

    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode)] 
    private static extern SafeFileHandle CreateFile(
     string lpFileName, 
     EFileAccess dwDesiredAccess, 
     EFileShare dwShareMode, 
     IntPtr lpSecurityAttributes, 
     ECreationDisposition dwCreationDisposition, 
     EFileAttributes dwFlagsAndAttributes, 
     IntPtr hTemplateFile); 
Cuestiones relacionadas