2010-07-21 3 views
1

Mon utilisation de ConcurrentQueue est-elle ici entre 2 threads? Je voulais vérifier que je n'ai pas besoin de "verrouiller" n'importe où explicitement. En regard particulier sur les lignes où j'ai dans les commentaires que je déposer un paquet ici ...Mon utilisation de ConcurrentQueue est-elle ici entre 2 threads?

public class PacketCapturer 
{ 

private static ConcurrentQueue<Packet> _packetQueue = new ConcurrentQueue<Packet>(); 

public PacketCapturer(IPHostEntry proxyDns, ref BackgroundWorker bw) 
{ 
    // start the background thread 
    var backgroundThread = new System.Threading.Thread(BackgroundThread); 
    backgroundThread.Start(); 

    // Start Packet Capture with callback via PacketCapturerCallback 
} 

private void PacketCapturerCallback(Packet packet) 
{ 
    _packetQueue.Enqueue(packet); 
} 

private static void BackgroundThread() 
{ 
    while (!BackgroundThreadStop) 
    { 
     if (_packetQueue.Count == 0) Thread.Sleep(250); 
     else 
     { 
      ConcurrentQueue<Packet> ourQueue; 
      ourQueue = _packetQueue; // COULD I DROP A PACKET BETWEEN HERE 
      _packetQueue = new ConcurrentQueue<Packet>();  // AND HERE??? 

      Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count); 
     } 
    } 
} 
+0

Vous n'utilisez jamais la file d'attente simultanément (c'est-à-dire que vous n'y accédez jamais à partir du second thread), alors pourquoi utilisez-vous une ConcurrentQueue? – Gabe

Répondre

1

Edit: Je pense que Mark/Gabe avez raison que vous ne perdrez pas Packets. Je laisserai le reste juste pour référence au cas où quelqu'un d'autre pourrait intervenir.


Simplement, oui. Vous pourriez perdre un ou plusieurs paquets là-bas. Vous pourriez vouloir regarder dans quelles méthodes ConcurrentQueue offre pour obtenir/enlever une partie de celui-ci comme cela ressemble à ce que vous voulez faire.

Pourquoi ne pas TryDequeue jusqu'à ce qu'il retourne false:

else 
    { 
     Queue<Packet> ourQueue = new Queue<Packet>(); //this doesn't need to be concurrent unless you want it to be for some other reason. 
     Packet p; 
     while(_packetQueue.TryDequeue(out p)) 
     { 
      ourQueue.Enqueue(p); 
     } 

     Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count); 
    } 
+0

@Chad - soin d'expliquer * comment * "oui"? –

+0

Marc ... Je veux dire oui, il peut perdre un ou plusieurs paquets ...Voici comment: Thread 1: ourQueue = _packetQueue; Thread 2: _packetQueue.Enqueue (paquet); // ce paquet est perdu ... plus Les Enqueues peuvent arriver ici Thread 1: _packetQueue = new ConcurrentQueue (); – Chad

+0

Le concept d'une file d'attente que je peux ajouter et retirer de deux (2) threads en même temps me semble bon - Alors Queue supporte-t-elle cette utilisation simultanée ici? Devrait-il être ConcurrentQueue? – Greg

2

Non, n'est pas correct. Tout d'abord, si vous modifiez la référence comme ceci dans un thread concurrent, le _packetQueue doit être marqué comme volatile pour empêcher les optimisations du compilateur et du codegen de ne jamais voir la modification. Le cahnging de l'utilitaire _packetQueue se produit normalement en tant que Interlocked.CompareExchange, mais cela est moins critique pour votre utilisation. Mais pour plus de worying est le motif de changer l'instance de packetQueue comme ceci dans le thread d'arrière-plan. Quel est le but de ceci? Il a une odeur de code horible ...

mis à jour

Ce que je fais ususaly est la suivante:

fil Producteur (s):

Producer() { 
... 
lock(_sharedQueue) { 
    _sharedQueue.Enqueue(something); 
} 
... 
} 

fil à la consommation:

consumer (...) { 
... 
var Something[] toProcess = null; 
lock(_sharedQueue) 
{ 
    toProcess = _sharedQueue.Toarray(); 
    _sharedQueue.Clear(); 
} 
// Process here the toProcess array 
... 
} 

C'était assez bon pour chaque singl L'utilisation que j'ai jamais eu. Le traitement ne se fait pas sous la serrure, donc le verrouillage est minime. Il n'y a aucun besoin pour le ConcurrentQueue de fantaisie, une collection simple .Net 2.0 est assez. Habituellement, j'utilise un objet de verrouillage dédié, pour une bonne pratique, au lieu de verrouiller l'instance de file d'attente réelle.

+0

merci - J'aime la suggestion du Tchad si cela fonctionne bien – Greg

+0

Quel est votre objectif? Visez-vous une file d'attente libre de verrou? La copie des éléments ne sera pas verrouillée, le ConcurrentQueue * utilisera le verrouillage en interne. Atteindre une file d'attente sans verrou n'est * pas * trivial par tous les moyens, et je doute fortement que vous en ayez vraiment besoin. La structure sans verrou est nécessaire dans les systèmes avec des cœurs +64 et un travail intensif et critique, comme un programmateur Os. –

+0

Je étais juste après un moyen de découpler le rappel de capture de paquets vraiment, afin de garder le callback lui-même très court, et avoir la chose qui utilise les données sur un autre thread – Greg

1

Je ne pense pas que vous pouvez déposer un paquet, comme évidemment un thread va déréférencer à soit l'ancien ou nouveau. Le bord-case est que vous obtenez plus de données arrivent dans ourQueueaprès vous pensez que vous les avez échangés, ou vous pourriez aussi ne pas remarquer le changement de référence dans la boucle Sleep (mais: avec 2 discussions, le seul thread qui change la référence est ce fil, donc pas nécessairement un problème).

TBH, si vous avez 2 fils ici, pourquoi ne pas simplement lock ou utiliser ReaderWriterLock? Il sera beaucoup plus simple de comprendre ce qui se passe quand ...

+0

merci - J'aime la suggestion du Tchad ci-dessous si cela fonctionne bien – Greg

+0

Marc - que pensez-vous de l'utilisation de lock ou ReadWriterLock, ou d'aller avec l'exemple que Chad a donné? – Greg

+0

@Greg - si vous n'avez * pas * à échanger la file d'attente (pour une raison quelconque), alors c'est idéal. Avec la mise à jour de Chad, ça a l'air bien. –