Question

J'ai un fichier qui est une représentation XML de certaines données extraites d'un service Web et mises en cache localement dans une application Web.L'idée étant que ces données sont très statique, mais juste pourrait changement.Je l'ai donc configuré pour mettre en cache un fichier et j'ai collé un moniteur dessus pour vérifier s'il a été supprimé.Une fois supprimé, le fichier sera actualisé à partir de sa source et reconstruit.

Cependant, je rencontre maintenant des problèmes, car évidemment, dans un environnement multithread, il tombe lorsqu'il essaie d'accéder aux données alors qu'il est encore en train de lire/écrire le fichier.

Cela me rend confus, car j'ai ajouté un objet contre lequel verrouiller, et celui-ci est toujours verrouillé pendant la lecture/écriture.J'avais cru comprendre que toute tentative d'accès à partir d'autres threads serait invitée à « attendre » jusqu'à ce que le verrou soit libéré ?

Juste pour vous faire savoir, je suis vraiment nouveau dans le développement multithread, donc je suis tout à fait prêt à accepter que c'est une erreur de ma part :)

  • Est-ce que j'ai raté quelque chose ?
  • Quelle est la meilleure stratégie d’accès aux fichiers dans un environnement multithread ?

Modifier

Désolé - j'aurais dû dire que cela utilise ASP.NET 2.0 :)

Était-ce utile?

La solution

Voici le code que j'utilise pour m'assurer qu'un fichier n'est pas verrouillé par un autre processus.Ce n'est pas infaillible à 100 %, mais cela fait le travail la plupart du temps :

    /// <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;
    }

Évidemment, vous pouvez modifier les délais d'attente et les tentatives en fonction de votre application.Je l'utilise pour traiter d'énormes fichiers FTP dont l'écriture prend un certain temps.

Autres conseils

Si vous verrouillez un objet stocké en tant que statique alors le verrou devrait fonctionner pour tous les threads du même domaine d'application, mais vous devrez peut-être télécharger un exemple de code afin que nous puissions examiner les lignes incriminées.

Cela dit, une idée serait de vérifier si IIS est configuré pour fonctionner dans Jardin Web mode (c'est-à-direplus d'un processus exécutant votre application), ce qui briserait votre logique de verrouillage.Bien que vous puissiez résoudre une telle situation avec un mutex, il serait plus facile de reconfigurer votre application pour qu'elle s'exécute en un seul processus, bien qu'il serait sage de vérifier les performances avant et après avoir modifié les paramètres du jardin Web, car cela peut potentiellement affecter performance.

Vous pourriez peut-être créer le fichier avec un nom temporaire ("data.xml_TMP"), et quand il sera prêt, changer le nom pour celui qu'il est censé être.De cette façon, aucun autre processus n’y accédera avant qu’il ne soit prêt.

OK, j'ai travaillé là-dessus et j'ai fini par créer un module de test de stress pour essentiellement détruire mon code à partir de plusieurs threads (Voir la question connexe).

À partir de ce moment, il était beaucoup plus facile de trouver des trous dans mon code.Il s'avère que mon code n'était pas vraiment loin, mais il y avait un certain chemin logique dans lequel il pouvait entrer, ce qui provoquait essentiellement l'empilement des opérations de lecture/écriture, ce qui signifie que si elles n'étaient pas effacées à temps, ce serait allez boum !

Une fois que j'ai retiré cela, j'ai refait mon test de stress, tout a bien fonctionné !

Donc je n'ai vraiment rien fait spécial dans mon code d'accès au fichier, je me suis juste assuré d'utiliser lock déclarations le cas échéant (c.-à-d.en lisant ou en écrivant).

Que diriez-vous d'utiliser AutoResetEvent communiquer entre les threads?J'ai créé une application console qui crée un fichier d'environ 8 Go dans createfile méthode, puis copiez ce fichier dans main méthode

 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();
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top