AsyncBackgroundTaskManager
and AsyncObservableCollection
is a BackgroundTaskManager
/ObservableCollection
that supports a list and runs asynchronously.
Introduction
Cinch included a class named BackgroundTaskManager
, which offloads CPU hogging work from UI thread, and notifies the UI when a work is completed.
BackgroundTaskManager
uses BackgroundWorker
to do the tricks, but it is easier to use:
BackgroundTaskManager<string> _bgWorker = new BackgroundTaskManager<string>(
() => { return CPUHoggingWork(); }
(result) => { Result = result; }
);
The advantage of using BackgroundTaskManager
is that you can write all related code in one place, and you won’t mis-write UI updating code in non-UI thread (and vice versa).
BackgroundTaskManager
worked for me in most cases except when I wanted to use it to return a list of items, BackgroundTaskManager
only allows return all items when the computation is completed, but I wanted it to return an item as soon as it’s constructed. So the result is AsyncBackgroundTaskManager
, a BackgroundTaskManager
that supports async tasks.
Although ObservableCollection
has a constructor that takes IEnumerable<T>
, it still blocks the thread when it’s loading, so after I completed the AsyncBackgroundTaskManager
, I derived a new ObservableCollection
class, named AsyncObservableCollection
, which uses AsyncBackgroundTaskManager
to populate its items in a separated thread.
How to Use?
The constructor of AsyncBackgroundTaskManager
takes a couple parameters:
taskFunc (Func<IEnumerable<T>>)
or taskEnumerable (IEnumerable<T>)
- Specify what to return here, you can use linq or a method that returns IEnumerable<T>
.
newItemAction (Action<T>)
– Runs in UI Thread when a new item is prepared.
removeItemAction (Action<T>)
– Optional, Runs in UI Thread when an item should be removed.
completeAction (Action<IList<T>>)
– Runs in UI Thread when taskFunc
is completed.
_bgWorker2 = new AsyncBackgroundTaskManager<StringModel>(DispatcherPriority.SystemIdle,
from fi in new DirectoryInfo(_path).EnumerateFiles("*", SearchOption.AllDirectories)
select new StringModel(fi.FullName),
(model) => { InternalCollection.Add(model); },
(model) => { InternalCollection.Remove(model); },
(modelList) => { _aborted = (modelList == null); });
_bgWorker2.RunBackgroundTask();
The constructor of AsyncObservableCollection
takes similar parameters, taskFunc
and completeAction
:
_internalCollection2 = new AsyncObservableCollection<StringModel>(
from fi in new DirectoryInfo(_path).EnumerateFiles("*", SearchOption.AllDirectories)
select new StringModel(fi.FullName),
(modelList) => { Debug.WriteLine(modelList.Count.ToString() + " polled."); }
);
_internalCollection2.Load(false);
How It Works?
There are only 158 lines in the AsyncBackgroundTaskManager
class, so you won't find any magic there.
The code below appears many times. It alerts the UI thread when the collection should be changed.
_dispatcher.Invoke(_priority, new ThreadStart(() =>
{
}));
So compared with BackgroundTaskManager
, instead of using BackgroundWorker
, it creates a new Thread
to invoke TaskFunc
, then loop the IEnumerator
for items. Then for each item returned, it alerts the UI Thread using NewItemAction
.
Further Improvements
Performance is affected because it invokes the UI thread every time when a new item is returned, this can be avoided if I change the component so instead of pushing new item to UI thread, the UI thread creates a DispatcherTimer
to pull new items when it has time.
Reference
This article has been posted on CodeProject. You can find a list of my articles here.