2009-06-03 4 views
1

J'ai un thread qui produit des données sous la forme d'un objet simple (record). Le thread peut produire un millier d'enregistrements pour chacun qui passe avec succès un filtre et est effectivement mis en file d'attente. Une fois l'objet mis en file d'attente, il est en lecture seule.Producteur-consommateur à double file d'attente dans .NET (vidage de variable membre forçant)

J'ai un verrou, que j'accepte une fois que l'enregistrement a passé le filtre, et j'ajoute l'élément à l'arrière de la file d'attente producteur. Sur le thread consommateur, j'acquiche le verrou, confirme que la file d'attente producer_queue n'est pas vide, définit consumer_queue pour égaler producer_queue, crée une nouvelle file d'attente (vide) et la place sur producer_queue. Sans plus de verrouillage, je file consumer_queue jusqu'à ce qu'il soit vide et répète.

Tout fonctionne parfaitement sur la plupart des machines, mais sur un serveur dual-quad particulier je vois dans les itérations ~ 1/500k un objet qui n'est pas complètement initialisé quand je le lis hors de consumer_queue. La condition est si fugace que lorsque je jette l'objet après avoir détecté la condition, les champs sont corrects 90% du temps. Donc, ma question est la suivante: comment puis-je m'assurer que les écritures sur l'objet sont vidées dans la mémoire principale lorsque la file d'attente est permutée?

Edit:

Sur le fil de producteur: (producer_queue ci-dessus est m_fillingQueue, consumer_queue ci-dessus est m_drainingQueue)

private void FillRecordQueue() { 
    while (!m_done) { 
    int count; 
    lock (m_swapLock) { 
     count = m_fillingQueue.Count; 
    } 
    if (count > 5000) { 
     Thread.Sleep(60); 
    } else { 
     DataRecord rec = GetNextRecord(); 
     if (rec == null) break; 
     lock (m_swapLock) { 
     m_fillingQueue.AddLast(rec); 
     } 
    } 
    } 
} 

Dans le fil du consommateur:

private DataRecord Next(bool remove) { 
    bool drained = false; 
    while (!drained) { 
    if (m_drainingQueue.Count > 0) { 
     DataRecord rec = m_drainingQueue.First.Value; 
     if (remove) m_drainingQueue.RemoveFirst(); 
     if (rec.Time < FIRST_VALID_TIME) { 
     throw new InvalidOperationException("Detected invalid timestamp in Next(): " + rec.Time + " from record " + rec); 
     } 
     return rec; 
    } else { 
     lock (m_swapLock) { 
     m_drainingQueue = m_fillingQueue; 
     m_fillingQueue = new LinkedList<DataRecord>(); 
     if (m_drainingQueue.Count == 0) drained = true; 
     } 
    } 
    } 
    return null; 
} 

Le consommateur est le taux -limité, de sorte qu'il ne peut pas devancer le consommateur.

Le comportement que je vois est que parfois le champ Heure lit comme DateTime.MinValue; au moment où je construis la chaîne pour lancer l'exception, cependant, c'est parfaitement bien.

+0

Je ne comprends pas bien - êtes-vous en train de dire que vous utilisez la même variable pour la file d'attente de production que la file d'attente grand public? C'est la seule pièce qui se démarque de moi. Certains exemples de code peuvent aider. –

+0

D'après votre description du verrouillage, je ne vois pas comment 'consumer_queue' peut contenir des objets incomplètement initialisés. Est-il possible pour vous d'envoyer des fragments de code pour répondre à cette question? – jerryjvl

Répondre

0

Si l'on suppose ce sont en fait les seules méthodes qui interagissent avec la variable m_fillingQueue, et que DataRecord ne peuvent pas être modifiés après GetNextRecord() crée, puis le code (en lecture seule propriétés, espérons?) Au moins sur le visage de celui-ci semble être correct.

Dans ce cas, je suggère que la réponse de GregC serait la première chose à vérifier; assurez-vous que la machine défaillante est entièrement mise à jour (OS/drivers/.NET Framework), car l'instruction lock doit impliquer toutes les barrières de mémoire requises pour s'assurer que la variable rec est complètement vidée de tous les caches avant que l'objet ne soit ajouté à la liste .

+0

Windows Update FTW. Bien qu'aucune mise à jour de microcode ou de pilote de processeur n'ait été disponible, l'installation de .NET SP1 a résolu le problème. –

+0

Bon à entendre. Mon patron est toujours sceptique quand je dis des choses comme ça. Corrigez-le dans les logiciels, dit-il. Un pour l'équipe. – GregC

+0

Puis-je me renseigner sur quel morceau de .NET a été amélioré? (1.1, 2.0, 3.0, 3.5?) – GregC

2

Avez-vous essayé l'évidence: la mise à jour du microcode est-elle appliquée sur la boîte fantaisie à 8 cœurs (via la mise à jour du BIOS)? Avez-vous exécuté Windows Updates pour obtenir le dernier pilote de processeur? Au premier coup d'œil, on dirait que vous verrouillez vos conteneurs. Je recommande donc l'approche systémique, car il semble que vous ne voyez pas ce problème dans une boîte bicœur.

Questions connexes