Question

Quelle est la meilleure façon de construire un programme qui est sûr de fil en termes qu'il a besoin d'écrire des valeurs doubles dans un fichier. Si la fonction qui sauvegarde les valeurs via StreamWriter est appelée par plusieurs threads? Quelle est la meilleure façon de le faire?


code de commentaire:

    static void Main()
    {
        List<double> Values = new List<double>();
        StreamWriter writer = new StreamWriter("test.out");

        for (int i = 0; i < 1000; i++) Values.Add(i);

        foreach (double V in Values)
        {
            ThreadPool.QueueUserWorkItem(delegate(object state) { SaveValues(writer, V); }, V);
        }
    }

    static void SaveValues(StreamWriter writer, double Value)
    {
        lock (writer) writer.WriteLine(Value);
    } 
Était-ce utile?

La solution

Modifier, après décodage du code dans votre commentaire

Votre problème est pas avec verrouillage ou de fils, mais avec la capture de la variable de boucle. Ceci est un problème classique, tous vos fils exécuter avec un « capture » de la seule variable V, et au moment où les fils sont exécutés, il aura atteint sa valeur finale de 999. Sauf peut-être pour les premiers éléments de travail.

Alors, au lieu de:

foreach (double V in Values)
{  
   ThreadPool.QueueUserWorkItem(delegate(object state) 
        { SaveValues(writer, V); }, V); // use of captured V: almost always 999
}

utilisation

foreach (double V in Values)
{
    double W = V;
    ThreadPool.QueueUserWorkItem(delegate(object state)
       { SaveValues(writer, W); }, V);
}

ou, un peu plus concis,

foreach (double V in Values)
{
    double W = V;
    ThreadPool.QueueUserWorkItem((state) => SaveValues(writer, W));
}

En tant que variante de @ la réponse de Matti, il est recommandé de verrouillage sur un objet simple, séparé. Cela réduit le risque de tout verrouillage autre sur le même objet (le code StreamWriter lui-même, par exemple).

private StreamWriter writer = ...;         
private Object writerLock = new Object();  // don't expose through a property 
// or any other way

lock (writerLock)
{
    writer.Write(...);
}

Mais la façon dont vous avez mis en place la méthode SaveValues(StreamWriter writer, ...) rendre un peu plus compliqué. Il est préférable d'avoir un objet où writer et writerLock sont tous deux membres privés.

Autres conseils

TextWriter.Synchronized

  

Crée un wrapper thread-safe autour du TextWriter spécifié. ll écrit à l'emballage retourné sera thread-safe.

Verrouiller tout en y accéder.

lock (myStreamWriter) {
    myStreamWriter.Write(...);
}

qui se débarrasse du problème de plusieurs threads essayant d'y accéder une fois. Bien sûr, si vous devez vous assurer que les choses encore sont écrits dans l'ordre de plusieurs threads vous devez ajouter une logique supplémentaire.

En général, lorsque vous faites entrée ou de sortie, je préfère un producteur / file d'attente des consommateurs avec un seul consommateur qui fait l'écriture réelle de la sortie.

On peut se synchroniser avec un verrou sur le StreamWriter (ou un objet de synchronisation qui est utilisé lorsque des fils veulent avoir accès à la StreamWriter). Cependant, lorsque plusieurs threads tentent d'écrire, un seul sera en mesure de continuer.

Si cela est un problème, vous pouvez implémenter une file d'attente, et ajouter un fil d'écrivain. Le fil d'écrivain dequeue et écrire au StreamWriter. Les autres threads enqueue articles au lieu d'écrire des articles directement.

Vous devez avoir une certaine forme de synchronisation pour l'accès à cette file d'attente. Vous pouvez simplement le verrouiller lorsque chaque thread accède. Si vous n'êtes pas prudent, cependant, pourrait créer des problèmes de performance pire que vous deviez commencer.

Modifier

Il semble que Stephen Cleary a un lien vers une classe .NET existante, vous pouvez utiliser pour mettre en œuvre la file d'attente. Je vais laisser ma réponse car il explique pourquoi vous voulez faire le producteur / consommateur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top