Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not operate ObservableCollection in multi threads

It seems the ObservableCollection only support add, remove, clear operation from the UI thread, It throw Not Support Exception if it is operated by a NO UI thread. I tried to override methods of ObservableCollection, unfortunatly, I met lots of problems. Any one can provide me a ObservableCollection sample which can be operated by multi-threads? Many thanks!

like image 931
user25749 Avatar asked Oct 09 '08 12:10

user25749


3 Answers

Using the link provided by Kent, you could use the following code to modify a collection across threads:

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

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

If however you're just looking to modify the collection on your original thread you can try using a callback to your UI thread. I normally do something like this:

this.Dispatcher.Invoke(new MyDelegate((myParam) =>
{
  this.MyCollection.Add(myParam);
}), state);
like image 193
Mark Ingram Avatar answered Oct 24 '22 10:10

Mark Ingram


You've basically got to Invoke or BeginInvoke over to the UI thread to do those operations.

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
like image 44
Bob King Avatar answered Oct 24 '22 11:10

Bob King


Personally, I find Bob's style of answer easier to use than Mark's style of answer. Here are the C# WPF snippets for doing this:

  1. in your class's constructor, get the current dispatcher as you create your observable collections. Because, as you pointed out, modifications need to be done on the original thread, which may not be the main GUI thread. So Application.Current.Dispatcher isn't alwasys right, and not all classes have a this.Dispatcher.

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
    _data = new ObservableCollection<MyDataItemClass>();
    
  2. Use the dispatcher to Invoke your code sections that need the original thread.

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

That should do the trick for you. Though there are situations you might prefer .BeginInvoke instead of .Invoke.

like image 3
Jesse Chisholm Avatar answered Oct 24 '22 10:10

Jesse Chisholm