2010-10-19 3 views
2

J'ai suivi this tutorial pour créer une file d'attente prioritaire et l'a enveloppée avec une collection de blocage. J'ai un DataGrid que j'ai câblé à la file d'attente prioritaire sous-jacente qui émet des événements de changement. Je peux ajouter des éléments à la collection à partir du thread de l'interface utilisateur sans aucun problème, et il bloque lorsque le tampon est plein comme il est censé le faire.BlockingCollection + thread UI

Maintenant, comment consommer les articles? Voici ce que j'ai:

public DownloadViewModel() 
{ 
    Queue = new ConcurrentPriorityQueue<DownloadItem>(10); 
    Buffer = new BlockingCollection<KeyValuePair<int, DownloadItem>>(Queue, 10000); 

    Task.Factory.StartNew(() => 
    { 
     KeyValuePair<int, DownloadItem> item; 
     while(!Buffer.IsCompleted) 
     { 
      if(Buffer.TryTake(out item)) 
      { 
       // do something with the item 
      } 

      Thread.SpinWait(100000); 
     } 
    }); 
} 

Mais dès que j'ajouté que Task.Factory.StartNew peu, mon application prend soudainement 30 secondes avant que la fenêtre apparaît (avant qu'il ne soit instant), et quand je fais ajouter un article que je reçois l'exception

Ce type de CollectionView ne prend pas en charge les modifications apportées à sa SourceCollection à partir d'un thread différent du thread Dispatcher.

Ce que je comprends, mais est-il vraiment nécessaire de prendre les éléments en utilisant le fil de l'interface utilisateur? Cela ne va-t-il pas à l'encontre de l'objectif de BlockingCollection? Je veux créer 4 ou 8 consommateurs et les faire fonctionner en parallèle.

Comment cela est-il censé être fait?

+0

Il est vraiment nécessaire de mettre à jour uniquement un contrôle d'interface utilisateur à partir du thread d'interface utilisateur. C'est ce que l'exception vous dit. Oui, cela détruit rapidement l'utilité des schémas de threads élaborés. –

+0

@Hans: Bien sûr, mais lorsque la file d'attente de blocage tente de prendre un élément, la file d'attente sous-jacente émet une notification de changement, .. pourquoi cela ne peut-il pas être automatiquement renvoyé au thread d'interface utilisateur? Pourquoi les consommateurs devraient-ils être retenus? Peu importe ... Je suis prêt à accepter n'importe quel sol'n dès maintenant, même si c'est un peu moins efficace que ce que je pense qu'il devrait être. J'imagine que c'est un paradigme assez commun .. producteur-consommateur n'est rien de nouveau, il ne devrait pas être si difficile d'afficher les éléments restants à l'utilisateur? – mpen

+0

Rien n'est jamais automatique ou facile quand il s'agit de filetage. Collectez les résultats dans une liste et utilisez Dispatcher.BeginInvoke pour mettre à jour l'interface utilisateur. –

Répondre

2

Emballage l'événement CollectionChanged w/un répartiteur semble assez bien fonctionner ...

public bool TryAdd(KeyValuePair<int, T> item) 
{ 
    int pos = _queues.Take(item.Key + 1).Sum(q => q.Count); 
    _queues[item.Key].Enqueue(item.Value); 
    Interlocked.Increment(ref _count); 
    Dispatcher.BeginInvoke(
     new Action(
      () => 
      NotifyCollectionChanged(
       new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, pos)) 
     )); 
    return true; 
} 

juste dû tirer mon ConcurrentPriorityQueue de DispatcherObject. Je pense c'est comment c'est censé être fait.


encore plus facile, il suffit d'écrire la méthode NotifyCollectionChanged comme ceci:

private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e) 
{ 
    lock (CollectionChanged) 
    { 
     if (CollectionChanged != null) 
      Dispatcher.BeginInvoke(new Action(() => CollectionChanged(this, e))); 
    } 
} 

Et vous n'avez pas à litière vos autres méthodes avec BeginInvoke.

+0

Pourriez-vous éventuellement poster un exemple de code (de MainWindow.xaml.cs)? – Fulproof

+0

@Fulproof: Impossible de trouver un exemple qui utilise cette classe, mais voici la classe complète: http://goo.gl/9BWb7 – mpen

+0

merci, mais le code de classe que vous avez provoqué donne une erreur de compilation "Une référence d'objet est requise pour le champ non statique, méthode ou propriété 'System.Windows.Threading.Dispatcher.Invoke (System.Delegate, params object [])' " – Fulproof

0

[Après avoir commenté la question, puis]

Vous n'avez pas besoin de « prendre les éléments en utilisant le fil de l'interface utilisateur ». Cependant, toute mise à jour de l'interface utilisateur suite au traitement de l'élément dans la tâche consommatrice doit être envoyée au thread d'interface utilisateur. Séparez vos préoccupations!

Questions connexes