2009-01-26 6 views
3

Je suis complètement perplexe avec la façon dont ma file d'attente est fonction. Je tente (et échoue) d'écrire une petite application multithread pour collecter et afficher des données en C#. Après avoir lu Albahari's book et en utilisant le modèle Consumer/Producer , il décrit que j'ai obtenu la plupart de travail, sauf que mes données semblent être brouillées dans la file d'attente . Avant de file d'attente les champs de mon objet ont les valeurs suivantesPourquoi en C# est la file d'attente brouillant les données dans ses éléments?

timeStamp = 6
données [] = {4936, 9845, 24125, 44861}

Après avoir été dequeued les données ressemble

timeStamp = 6
données [] = {64791, 19466, 47772, 65405}

Je ne comprends pas pourquoi les valeurs dans les données archivées sont modifiées après la dequeue? Je suis perplexe, alors j'ai pensé que je le lancerais là-bas pour voir si quelqu'un peut me diriger dans la bonne direction pour corriger cela ou pour me diriger dans une direction différente pour continuer.


pertinentes du Code


objet personnalisé pour le stockage des données

objets pertinents et champs. La classe sensorData est une classe séparée utilisée pour stocker mes calculs. Les champs suivants sont utilisés pour configurer la file d'attente et les signaux entre les files d'attente de mise en file d'attente et de file d'attente de mise en file d'attente.

EventWaitHandle wh = new AutoResetEvent(false); 
Queue<sensorData> dataQ = new Queue<sensorData>(); 
object locker = new object(); 

Enqueue Méthode/Discussion

Ceci est mon thread de travail il calcule quatre sinusoïdes et les files d'attente le résultat pour le traitement. J'écris aussi les résultats dans un fichier, donc je sais ce qu'il a calculé.

private void calculateAndEnqueueData(BackgroundWorker worker, DoWorkEventArgs e) 
{ 
    int j = 0; 
    double time = 0; 
    double dist; 
    UInt16[] intDist = new UInt16[sensorData.bufSize]; 
    UInt16 uint16Dist; 

    // Frequencies of the four Sine curves 
    double[] myFrequency = { 1, 2, 5, 10 }; 

    // Creates the output file. 
    StreamWriter sw2 = File.CreateText("c:\\tmp\\QueuedDataTest.txt"); 

    // Main loop to calculate my Sine curves 
    while (!worker.CancellationPending) 
    { 
     // Calculate four Sine curves 
     for (int i = 0; i < collectedData.numberOfChannels; i++) 
     { 
      dist = Math.Abs(Math.Sin(2.0 * Math.PI * myFrequency[i] * time); 
      uint16Dist = (UInt16)dist; 
      intDist[i] = uint16Dist; 
     } 

     // Bundle the results and Enqueue them 
     sensorData dat = new sensorData(); 
     dat.data = intDist; 
     dat.TimeStamp = time; 
     dat.timeIndex = j; 

     lock (locker) dataQ.Enqueue(dat); 
     wh.Set 

     // Output results to file. 
     sw2.Write(j.ToString() + ", "); 
     foreach (UInt16 dd in dat.intData) 
     { 
      sw2.Write(dd.ToString() + ", "); 
     } 
     sw2.WriteLine(); 

     // Increments time and index. 
     j++; 
     time += 1/collectedData.signalFrequency; 

     Thread.Sleep(2); 
    } 
    // Clean up 
    sw2.Close(); 
    lock (locker) dataQ.Enqueue(null); 
    wh.Set(); 
    sw2.Close(); 
} 

ligne de l'exemple dans le fichier de sortie QueuedDataTest.txt

6, 4936, 9845, 24125, 44861,

Dequeue Méthode données

Cette La méthode supprime les éléments de la file d'attente et les écrit dans un fichier. Jusqu'à ce qu'un élément nul soit trouvé dans la file d'attente à quel point le travail est terminé.

private void dequeueDataMethod() 
    { 
     StreamWriter sw = File.CreateText("C:\\tmp\\DequeueDataTest.txt"); 

     while (true) 
     { 
      sensorData data = null; 

      // Dequeue available element if any are there. 
      lock (locker) 
       if (dataQ.Count > 0) 
       { 
        data = dataQ.Dequeue(); 
        if (data == null) 
        { 
         sw.Close(); 
         return; 
        } 
       } 

      // Check to see if an element was dequeued. If it was write it to file. 
      if (data != null) 
      { 
       sw.Write(data.timeIndex.ToString() + ", "); 
       foreach (UInt16 dd in data.data) 
        sw.Write(dd.ToString() + ", "); 
       sw.WriteLine(); 
      } 
      else 
      { 
       wh.WaitOne(); 
      } 
     } 

résultat de sortie après dequeueing les données et écrire dans DequeueDataTest.txt

6, 64791, 19466, 47772, 65405,


mises à jour 1 :

Emplacement des verrous dans le code actuel.


J'ai modifié le code pour placer des verrous autour des données d'écriture dans le fichier. Les blocs de code que j'ai verrouillés sont les suivants.

Dans le CalculateAndEnqueueData() méthode Je

lock (locker) dataQ.Enqueue(dat); 
wh.Set 

lock(locker) 
{ 
    sw2.Write(j.ToString() + ", "); 
    foreach (UInt16 dd in dat.intData) 
    { 
    sw2.Write(dd.ToString() + ", "); 
    } 
    sw2.WriteLine(); 
} 

Dans le dequeueDataMethod() J'ai deux zones avec des serrures la première est ici

lock(locker) 
    if (dataQ.Count > 0) 
    { 
     data = dataQ.Dequeue(); 
     if (data == null) 
     { 
      sw.Close(); 
      return; 
     } 
    } 

que je suppose que les serrures casier pour le code dans le bloc si. La deuxième est où j'écris le dans le fichier ici

lock (locker) 
{ 
    sw.Write(data.timeIndex.ToString() + ", "); 
    foreach (UInt16 dd in data.intData) 
     sw.Write(dd.ToString() + ", "); 
    sw.WriteLine(); 
    if (icnt > 10) 
    { 
     sw.Close(); 
     return; 
    } 
    this.label1.Text = dataQ.Count.ToString(); 
} 

C'est tout.


+1

Sûrement que vous pouvez faire le chemin de code, de manière plus simple et de démontrer encore le problème que vous êtes voyant. –

+0

vous verrouillez la file d'attente, mais où est la synchronisation sur votre StreamWriter ??? –

+0

Chaque thread écrit dans un fichier séparé. Donc je ne pensais pas que j'aurais besoin de synchronisation pour cette partie du fil. Ai-je tort? – Azim

Répondre

1

Est-ce parce que vous écrivez dans le même tableau UInt16[] intDist encore et encore? Ne devriez-vous pas utiliser des tableaux séparés pour chaque objet sensorData? (BTW, est sensorData.intData censé être sensorData.data dans votre exemple de code?)

CLARIFICATION:

Un seul tableau intDist est créé dans calculateAndEnqueueData(), si différents cas sensorData sont tous partagent le même tableau --- ce est ok si l'ajout + écriture et suppression + écriture se fait en lock-step; sinon, certains points de données peuvent être manquants/répétés.

SUGGESTION:

Populate sensorData instances directement, sans utiliser le tableau intDist, en calculateAndEnqueueData():

// create new sensorData instance 
    sensorData dat = new sensorData(); 
    dat.TimeStamp = time; 
    dat.timeIndex = j; 

    // Calculate four Sine curves 
    for (int i = 0; i < collectedData.numberOfChannels; i++) 
    { 
     dat.data[i] = (UInt16) Math.Abs(Math.Sin(2.0 * Math.PI * myFrequency[i] * time); 
    } 

    // enqueue 
    lock (locker) dataQ.Enqueue(dat); 
+0

oui, merci. Typo de couper mon code vers le bas. À l'origine, sensorData avait quelques champs supplémentaires que j'ai supprimés pour la question. – Azim

+0

Non, je ne pense pas parce que le thread de production, calculateAndEnqueueData, qui écrit dans le UInt16 [] produit les données correctes. EnqueueDataMethod – Azim

+0

est brouillé car il est écrit "en bloc" ... lors de la mise en file d'attente, le même tableau est en cours de modification (bien que pour un objet sensorData différent) par calculateAndEnqueueData, donc les mauvais résultats. –

9

Le problème est dû à l'absence de synchronisation sur le StreamWriter dans lequel vous écrivez. La commande n'est pas séquentielle.

+0

Oui, je dois placer un verrou autour du code qui écrit dans le fichier. Merci. C'est exactement ce dont j'avais besoin! – Azim

+0

Je pensais que vous avez dit que chaque thread écrit dans un fichier séparé? Je ne pense pas que la synchronisation de StreamWriter, qui est définie localement, soit le problème ici. Pouvez-vous vérifier cela? –

+0

@Zach Scrivena: oui chaque thread écrit dans un fichier différent (test.txt et test2.txt). Tout ce que je sais, c'est que verrouiller le casier autour de ce bloc de code semble aider. Je ne sais pas pourquoi. – Azim