2010-10-14 5 views
2

J'utilise le BlockingCollection pour un modèle de Producteur Consommateur et j'ai eu une excecption je pense à écrire un brevet dessus-seulement deux résultats dans google! le expection est « CompleteAdding ne peut pas être utilisé en même temps avec des ajouts à la collection » et il arrive quand je TryAdd sur e BlockingCollection comme suit:exception très unique pour BlockingCollection sur .net 4.0

public void EnqueueTask(T item) 
    { 
     if (!_cancellationTokenSource.IsCancellationRequested) 
     { 
      _workerQueue.Add(item); 
     } 
    } 

la CompleteAdding est appelée sur la Éliminez de la classe emballage consommateur-producteur :

public void Dispose() 
    { 
     if (!_IsActive) 
      return; 
     _IsActive = false; 
     _cancellationTokenSource.Cancel(); 
     _workerQueue.CompleteAdding(); 
     // Wait for the consumer's thread to finish. 
     for (int i = 0; i < _workers.Length; ++i) 
     { 
      Task t1 = Task.Factory.StartNew(() => 
      { 
       try 
       { 
        if (!_workers[i].Join(4000)) 
         LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose"); 
       } 
       catch (Exception ex) 
       { 
        OnLogged(ex.Message + ex.StackTrace); 
       } 
      }); 

     } 


     // Release any OS resources. 
    } 

Quelqu'un de Microsoft a une idée? devrais-je dormir après l'annulation et avant d'appeler le CompleteAdding?

+0

Quel est le type de l'exception? La documentation indique que 'Add' (et' TryAdd') lancera 'InvalidOperationException' si vous essayez d'ajouter un élément après que' CompleteAdding' a été appelé. Votre description n'est pas trop claire, mais si vous obtenez l'exception sur l'appel 'Add', je suppose que c'est ça. –

Répondre

5

Regardez ce morceau de code:

for (int i = 0; i < _workers.Length; ++i) 
    { 
     Task t1 = Task.Factory.StartNew(() => 
     { 
      try 
      { 
       if (!_workers[i].Join(4000)) << == Here 
        LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose"); 
      } 

En _workers[i].Join(4000), la valeur de i n'est pas ce que vous pensez qu'il est. Essayez à nouveau avec:

for (int i = 0; i < _workers.Length; ++i) 
    { 
     int j = i; // copy 
     Task t1 = Task.Factory.StartNew(() => 
     { 
      try 
      { 
       if (!_workers[j].Join(4000)) // j 
        LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose"); 
      } 

Dans votre version, la variable 'i' est capturée et toutes les tâches utilisent la même var. Tous sauf les premiers verront i == _workers.Length car ils sont exécutés une fois la boucle for terminée.

Il s'agit d'un problème de var capturé lambda + classique.

+2

Ah, la variable d'itération capturée classique. Bon endroit. –

+0

ce n'est pas compris dans la question mais si vous voulez dire le ++ i au lieu de l'i ++ - c'est un Typo. – user437631

+0

@user - prend une copie de i à l'intérieur du foreach; se référer seulement à cette copie. –