Pregunta

Tengo un archivo que es una representación XML de algunos datos que se toman de un servicio web y se almacenan en caché localmente dentro de una aplicación web.La idea es que estos datos sean muy estático, pero solo podría cambiar.Así que lo configuré para almacenar en caché un archivo y le puse un monitor para verificar si se había eliminado.Una vez eliminado, el archivo se actualizará desde su origen y se reconstruirá.

Sin embargo, ahora tengo problemas porque obviamente en un entorno de subprocesos múltiples falla al intentar acceder a los datos cuando todavía está leyendo/escribiendo el archivo.

Esto me confunde, porque agregué un objeto para bloquear, y esto siempre está bloqueado durante la lectura/escritura.¿Tenía entendido que al intento de acceso desde otros subprocesos se le indicaría que "esperara" hasta que se liberara el bloqueo?

Sólo para que sepas que soy realmente nuevo en el desarrollo de subprocesos múltiples, por lo que estoy totalmente dispuesto a aceptar que esto es un error de mi parte :)

  • ¿Me estoy perdiendo de algo?
  • ¿Cuál es la mejor estrategia de acceso a archivos en un entorno multiproceso?

Editar

Lo siento, debería haber dicho que esto está usando ASP.NET 2.0 :)

¿Fue útil?

Solución

Aquí está el código que uso para asegurarme de que otro proceso no bloquee un archivo.No es 100% infalible, pero hace el trabajo la mayor parte del tiempo:

    /// <summary>
    /// Blocks until the file is not locked any more.
    /// </summary>
    /// <param name="fullPath"></param>
    bool WaitForFile(string fullPath)
    {
        int numTries = 0;
        while (true)
        {
            ++numTries;
            try
            {
                // Attempt to open the file exclusively.
                using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite, 
                    FileShare.None, 100))
                {
                    fs.ReadByte();

                    // If we got this far the file is ready
                    break;
                }
            }
            catch (Exception ex)
            {
                Log.LogWarning(
                   "WaitForFile {0} failed to get an exclusive lock: {1}", 
                    fullPath, ex.ToString());

                if (numTries > 10)
                {
                    Log.LogWarning(
                        "WaitForFile {0} giving up after 10 tries", 
                        fullPath);
                    return false;
                }

                // Wait for the lock to be released
                System.Threading.Thread.Sleep(500);
            }
        }

        Log.LogTrace("WaitForFile {0} returning true after {1} tries",
            fullPath, numTries);
        return true;
    }

Obviamente, puede modificar los tiempos de espera y los reintentos para adaptarlos a su aplicación.Lo uso para procesar archivos FTP enormes que tardan un poco en escribirse.

Otros consejos

Si está bloqueando un objeto almacenado como estático entonces el bloqueo debería funcionar para todos los subprocesos en el mismo dominio de aplicación, pero quizás necesite cargar un código de muestra para que podamos ver las líneas ofensivas.

Dicho esto, una idea sería comprobar si IIS está configurado para ejecutarse en Jardín web modo (es decir,más de 1 proceso ejecutando su aplicación) lo que rompería su lógica de bloqueo.Si bien podría solucionar esta situación con un mutex, sería más fácil reconfigurar su aplicación para que se ejecute en un solo proceso, aunque sería prudente verificar el rendimiento antes y después de modificar la configuración del jardín web, ya que puede afectar potencialmente actuación.

Quizás podrías crear el archivo con un nombre temporal ("data.xml_TMP") y, cuando esté listo, cambiar el nombre al que se supone que debe ser.De esa manera, ningún otro proceso accederá a él antes de que esté listo.

Bien, he estado trabajando en esto y terminé creando un módulo de prueba de estrés para básicamente eliminar la mierda de mi código de varios subprocesos (Ver pregunta relacionada).

A partir de este momento fue mucho más fácil encontrar agujeros en mi código.Resulta que mi código en realidad no estaba muy lejos, pero había una cierta ruta lógica en la que podía ingresar y que básicamente causaba que las operaciones de lectura/escritura se acumularan, lo que significa que si no se borraban a tiempo, ¡Vaya boom!

Una vez que lo saqué, realicé mi prueba de estrés nuevamente, ¡todo funcionó bien!

Entonces realmente no hice nada. especial en mi código de acceso a archivos, solo me aseguré de haber usado lock declaraciones cuando corresponda (es decir,al leer o escribir).

¿Qué tal usar AutoResetEvent para comunicarse entre hilos?Creé una aplicación de consola que crea un archivo de aproximadamente 8 GB en createfile método y luego copie ese archivo en main método

 static AutoResetEvent waitHandle = new AutoResetEvent(false);
    static string filePath=@"C:\Temp\test.txt";
    static string fileCopyPath=@"C:\Temp\test-copy.txt";
    static void Main(string[] args)
    {
        Console.WriteLine("in main method");
        Console.WriteLine();
        Thread thread = new Thread(createFile);
        thread.Start();

        Console.WriteLine("waiting for file to be processed ");
        Console.WriteLine();
        waitHandle.WaitOne();
        Console.WriteLine();

        File.Copy(filePath, fileCopyPath);
        Console.WriteLine("file copied ");

    }


    static void createFile()
    {

        FileStream fs= File.Create(filePath);            
        Console.WriteLine("start processing a file "+DateTime.Now);
        Console.WriteLine();
        using (StreamWriter sw = new StreamWriter(fs))
        {
            for (long i = 0; i < 300000000; i++)
            {
                sw.WriteLine("The value of i is " + i);

            }
        }
        Console.WriteLine("file processed " + DateTime.Now);
        Console.WriteLine();

        waitHandle.Set();
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top