Не может работать ObservableCollection в нескольких потоках - PullRequest
7 голосов
/ 09 октября 2008

Кажется, что ObservableCollection поддерживает только операции добавления, удаления, удаления из потока пользовательского интерфейса. Она исключает исключение поддержки, если она управляется потоком NO UI. Я попытался переопределить методы ObservableCollection, к сожалению, я столкнулся с множеством проблем. Кто-нибудь может предоставить мне образец ObservableCollection, который может работать с несколькими потоками? Большое спасибо!

Ответы [ 4 ]

7 голосов
/ 09 октября 2008

Используя ссылку, предоставленную Kent , вы можете использовать следующий код для изменения коллекции между потоками:

while (!Monitor.TryEnter(_lock, 10))
{
   DoEvents();
}

try
{
   //modify collection
}
finally
{
   Monitor.Exit(_lock);
}

Если, однако, вы просто хотите изменить коллекцию в исходном потоке, вы можете попытаться использовать обратный вызов для потока пользовательского интерфейса. Я обычно делаю что-то вроде этого:

this.Dispatcher.Invoke(new MyDelegate((myParam) =>
{
  this.MyCollection.Add(myParam);
}), state);
3 голосов
/ 15 ноября 2012

Лично я считаю, что стиль ответа Боба проще в использовании, чем стиль ответа Марка. Вот фрагменты C # WPF для этого:

  1. в конструкторе вашего класса, получите текущий диспетчер при создании ваши наблюдаемые коллекции. Потому что, как вы указали, модификации должны быть сделано в оригинальном потоке, который не может быть основным GUI-потоком. Так что Application.Current.Dispatcher не всегда прав, и не у всех классов есть this. Диспетчер .

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
    _data = new ObservableCollection<MyDataItemClass>();
    
  2. Используйте диспетчер для вызова разделов кода которые нуждаются в оригинальной теме.

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
    

Это должно помочь тебе. Хотя в некоторых ситуациях вы можете предпочесть .BeginInvoke вместо .Invoke .

3 голосов
/ 09 октября 2008

Для выполнения этих операций вы в основном должны вызывать Invoke или BeginInvoke в потоке пользовательского интерфейса.

Public Delegate Sub AddItemDelegate(ByVal item As T)

Public Sub AddItem(ByVal item As T)
    If Application.Current.Dispatcher.CheckAccess() Then
        Me.Add(item)
    Else
        Application.Current.Dispatcher.Invoke(Threading.DispatcherPriority.Normal, New AddItemDelegate(AddressOf AddItem), item)
    End If
End Sub
2 голосов
/ 13 января 2013

Возможно, вы захотите изучить мой ответ на это, возможно, - но please note, что код пришел отсюда и не может быть зачислен мне. Хотя я пытался реализовать это в VB. : Оригинальный сайт

Я использовал это для заполнения списка WPF из класса, в котором ObservableCollectionEx асинхронно заполняется из базы данных доступа. Это работает.

public class ObservableCollectionEx<T> : ObservableCollection<T>
{
   // Override the event so this class can access it
   public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler    
 CollectionChanged;

   protected override void OnCollectionChanged    (System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
   {
  // Be nice - use BlockReentrancy like MSDN said
  using (BlockReentrancy())
  {
     System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
     if (eventHandler == null)
        return;

     Delegate[] delegates = eventHandler.GetInvocationList();
     // Walk thru invocation list
     foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
     {
        DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
        // If the subscriber is a DispatcherObject and different thread
        if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
        {
           // Invoke handler in the target dispatcher's thread
           dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
        }
        else // Execute handler as is
           handler(this, e);
     }
  }
}
}
...