BlockingCollection + UI Thread - c#

I've followed this tutorial, to create a priority queue and wrapped it with a blocking collection. I've got a DataGrid which I've wired to the underlying priority queue which emits change events. I can add items to the collection from the UI thread w/out a hitch, and it blocks when the buffer is full as it's supposed to.
Now how do I consume the items? Here's what I've got:
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);
}
});
}
But as soon as I added that Task.Factory.StartNew bit, my app suddenly takes 30 seconds before the window appears (before it was instant), and when I do add an item I get the exception
This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
Which I understand, but is it really necessary to take the items using the UI thread? Doesn't that defeat the whole purpose of using this BlockingCollection? I want to create 4 or 8 consumers and have them run in parallel.
How is this supposed to be done?

Wrapping the CollectionChanged event w/ a dispatcher seems work pretty well...
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;
}
Just had to derive my ConcurrentPriorityQueue from DispatcherObject. I think this is how it's supposed to be done.
Easier yet, just write the NotifyCollectionChanged method like this:
private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
{
lock (CollectionChanged)
{
if (CollectionChanged != null)
Dispatcher.BeginInvoke(new Action(() => CollectionChanged(this, e)));
}
}
And then you don't have to litter your other methods with BeginInvoke.

[After commenting on the question, then]
You don't need to "take the items using the UI thread". However, any updates to the UI as a result of processing the item in the consuming task need to be dispatched to the UI thread. Separate your concerns!

Related

Running Action asynchronous on Dispatcher won't finish or freezes UI

I just want to run the Drop action asynchronous to display a busy Dialog while moving large amounts. Because the source collection can only be accessed by the Dispatcher, I need to invoke it.
This way the awaited invoke never finishes / the dialog will never be closed.
Using Invoke instead of InvokeAsync here will cause a NotSupportedException for the same reason mentioned above.
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
await Task.Run(() =>
{
// This would crash:
// Dispatcher.CurrentDispatcher.Invoke(() =>
await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
var data = dropInfo.Data as SomeObject;
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (data != null)
{
// Operate with collection
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
// Operate with collection
}
});
});
// Never reaches this point
MainViewModel.Current.CloseDialog();
}
And this way the UI just freezes, but finishes after work is done:
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
var data = dropInfo.Data as SomeObject;
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (data != null)
{
// Operate with collection
}
else if (dropInfo.Data is IEnumerable<SomeObject
{
// Operate with collection
}
});
MainViewModel.Current.CloseDialog();
}
What am I missing out or how could I get this to work like intended?
EDIT
First thanks to your answers and explanations, very helpful!
I tried this now and the collection is at the end of the method updated, the UI won't freezes and the Dialog displays correctly, but the collection won't get updated in the UI neither in the ViewModel.
When I let it operate directly with the collection (on the UI Thread) it will be updated in both directly.
Btw. the Drop method is in the matching ViewModel, but there I can only acces the binded collection as read only due to validation and checking etc. So I can only add/remove items by custom methods, that would be overkill.
Here in the awaited Task Resharper says: Implicitly captured closure: collection
And at the awaited invoke: Implicitly captured closure: dropInfo
But that should be ok, because the operation doesnt takte that long.
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (collection == null)
return;
// ObservableCollection needed for .Move() extension
var collectionCopy = new ObservableCollection<SomeObject>(collection.ToList());
await Task.Run(() =>
{
var data= dropInfo.Data as SomeObject;
if (data!= null)
{
// operate with collectionCopy (Move item)
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
// operate with collectionCopy (Move items)
}
});
var dispatcher = Dispatcher.CurrentDispatcher;
await dispatcher.InvokeAsync(() =>
{
collection = collectionCopy;
// Just tried this but didn't work
RaisePropertyChanged(nameof(collection));
});
// collection is updated at this point
MainViewModel.Current.CloseDialog();
}
UPDATE ---------------------------------------------------------------------
Created and upload a sample to show the issue: Click
What am I missing out or how could I get this to work like intended?
As others have noted, Task.Run will execute work on a background thread, and Dispatcher.InvokeAsync will turn around and execute work on a UI thread. So, your code is not actually doing any real work anywhere other than the UI thread, which is why it's "blocking".
The cleanest solution is to copy all the necessary information out of the UI objects while on the UI thread, do any background work on the thread pool, and finally (if necessary) copy any results back into the UI objects.
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
// First, copy the data out of the UI objects.
List<SomeObject> list;
var data = dropInfo.Data as SomeObject;
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (collection != null)
{
list = collection.ToList();
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
list = ((IEnumerable<SomeObject>)dropInfo.Data).ToList();
}
// Then do the background work.
await Task.Run(() =>
{
// Operate with `list`
});
// Finally, update the UI objects after the work is complete.
MainViewModel.Current.CloseDialog();
}
You're running all this code on the UI dispatcher
await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
What you NEED to do is...
Run all your work in here await Task.Run(() =>
After you're finished, update the UI using the dispatcher.
By the way, if you're updating bound properties that implement INotifyPropertyChanged, you don't even have to use the dispatcher. Update the properties from whatever thread. The Binding will automatically marshall updates onto the UI thread.
The Dispatcher you're using in your code is the UI Dispatcher. That means, when you dispatch a method onto it, you are executing the method on the UI thread. ONLY ever use the Dispatcher at the last possible moment, and only use it to
Update the UI from a different thread
Schedule UI work after application lifecycle events (see the DispatcherPriority enum)
One more problem...
Dispatcher.CurrentDispatcher retrieves the ... current dispatcher. What is the current dispatcher? It's the dispatcher for the current thread. In your first example, the current thread is a background thread. You want to use the dispatcher from the UI. Here's the first example, but tweaked...
public async void Drop(IDropInfo dropInfo)
{
// We are in the UI thread here!
MainViewModel.Current.ShowBusyDialog();
// as we are on the UI thread, no worries touching the collection here
var collection = (ObservableCollection<SomeObject>)
((ICollectionView)dropInfo.TargetCollection).SourceCollection;
// as we are on the UI thread, this is the UI dispatcher.
var dispatcher = Dispatcher.CurrentDispatcher;
await Task.Run(() =>
{
// We are on a background thread here!
var data = dropInfo.Data as SomeObject;
YourResults results = null;
if (data != null)
{
results = WhateverProcessingTakesALongTime();
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
results = SameHereIHaveNoIdea(dropInfo.Data);
}
// Now, let's update the UI on the UI thread.
await dispatcher.InvokeAsync(() =>
{
UpdateStuffWithStuff(collection, results);
});
});
MainViewModel.Current.CloseDialog();
}

How do I stop a progress bar when the last task has finished?

I have a UI program that updates slow-loading items. I managed to get the items to load in parallel, and show each item as soon as it has finished loading. However, I want to show a progress bar while loading, and hide it once all items are loaded.
private void refreshButton_Click(object sender, EventArgs e)
{
foreach (var item in Items)
{
progressBar1.Visible = true;
Task.Factory
.StartNew(() => DoNonUiwork(item))
.ContinueWith(antedecent =>
{
UpdateUI(antedecent.Result);
if ( /* if what? */ )
{
progressBar1.Visible = false;
}
}
, TaskScheduler.FromCurrentSynchronizationContext());
}
}
I could remember all my tasks and use Parallel.WaitAll() on them, but then I'd be block the UI thread, which is Not Nice.
I could setup a homebrew sync mechanism - maybe a counter that starts with int itemsToUpdate = Items.Count(), and then /* if what? */ becomes --itemsToUpdate== 0. It works, but doesn't feel very Task-like...
Is there a Tasks-native way to accomplish this?
You could use Task.WhenAll method for this purpose. or use TaskFactory.ContinueWhenAll and then you can marshall the control to UI thread.
Put your Parallel.WaitAll() in yet another task.
You could try firing off another Task that does Task.WaitAll() and notifies your UI after.
Create the parent task.
Attach all tasks to parent.
In parent continuation hide progress bar.

Parallel.ForEach freezing on last loop [duplicate]

More newbie questions:
This code grabs a number of proxies from the list in the main window (I couldn't figure out how to make variables be available between different functions) and does a check on each one (simple httpwebrequest) and then adds them to a list called finishedProxies.
For some reason when I press the start button, the whole program hangs up. I was under the impression that Parallel creates separate threads for each action leaving the UI thread alone so that it's responsive?
private void start_Click(object sender, RoutedEventArgs e)
{
// Populate a list of proxies
List<string> proxies = new List<string>();
List<string> finishedProxies = new List<string>();
foreach (string proxy in proxiesList.Items)
{
proxies.Add(proxy);
}
Parallel.ForEach<string>(proxies, (i) =>
{
string checkResult;
checkResult = checkProxy(i);
finishedProxies.Add(checkResult);
// update ui
/*
status.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
status.Content = "hello" + checkResult;
}
)); */
// update ui finished
//Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i));
});
}
I've tried using the code that's commented out to make changes to the UI inside the Parallel.Foreach and it makes the program freeze after the start button is pressed. It's worked for me before but I used Thread class.
How can I update the UI from inside the Parallel.Foreach and how do I make Parallel.Foreach work so that it doesn't make the UI freeze up while it's working?
Here's the whole code.
You must not start the parallel processing in your UI thread. See the example under the "Avoid Executing Parallel Loops on the UI Thread" header in this page.
Update: Or, you can simply create a new thread manuall and start the processing inside that as I see you have done. There's nothing wrong with that too.
Also, as Jim Mischel points out, you are accessing the lists from multiple threads at the same time, so there are race conditions there. Either substitute ConcurrentBag for List, or wrap the lists inside a lock statement each time you access them.
A good way to circumvent the problems of not being able to write to the UI thread when using Parallel statements is to use the Task Factory and delegates, see the following code, I used this to iterate over a series of files in a directory, and process them in a Parallel.ForEach loop, after each file is processed the UI thread is signaled and updated:
var files = GetFiles(directoryToScan);
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
Task task = Task.Factory.StartNew(delegate
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
Parallel.ForEach(files, currentFile =>
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
ProcessFile(directoryToScan, currentFile, directoryToOutput);
// Update calling thread's UI
BeginInvoke((Action)(() =>
{
WriteProgress(currentFile);
}));
});
}, tokenSource.Token); // Pass same token to StartNew.
task.ContinueWith((t) =>
BeginInvoke((Action)(() =>
{
SignalCompletion(sw);
}))
);
And the methods that do the actual UI changes:
void WriteProgress(string fileName)
{
progressBar.Visible = true;
lblResizeProgressAmount.Visible = true;
lblResizeProgress.Visible = true;
progressBar.Value += 1;
Interlocked.Increment(ref counter);
lblResizeProgressAmount.Text = counter.ToString();
ListViewItem lvi = new ListViewItem(fileName);
listView1.Items.Add(lvi);
listView1.FullRowSelect = true;
}
private void SignalCompletion(Stopwatch sw)
{
sw.Stop();
if (tokenSource.IsCancellationRequested)
{
InitializeFields();
lblFinished.Visible = true;
lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString());
}
else
{
lblFinished.Visible = true;
if (counter > 0)
{
lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString());
}
else
{
lblFinished.Text = "Nothing to resize";
}
}
}
Hope this helps!
If anyone's curious, I kinda figured it out but I'm not sure if that's good programming or any way to deal with the issue.
I created a new thread like so:
Thread t = new Thread(do_checks);
t.Start();
and put away all of the parallel stuff inside of do_checks().
Seems to be doing okay.
One problem with your code is that you're calling FinishedProxies.Add from multiple threads concurrently. That's going to cause a problem because List<T> isn't thread-safe. You'll need to protect it with a lock or some other synchronization primitive, or use a concurrent collection.
Whether that causes the UI lockup, I don't know. Without more information, it's hard to say. If the proxies list is very long and checkProxy doesn't take long to execute, then your tasks will all queue up behind that Invoke call. That's going to cause a whole bunch of pending UI updates. That will lock up the UI because the UI thread is busy servicing those queued requests.
This is what I think might be happening in your code-base.
Normal Scenario: You click on button. Do not use Parallel.Foreach loop. Use Dispatcher class and push the code to run on separate thread in background. Once the background thread is done processing, it will invoke the main UI thread for updating the UI. In this scenario, the background thread(invoked via Dispatcher) knows about the main UI thread, which it needs to callback. Or simply said the main UI thread has its own identity.
Using Parallel.Foreach loop: Once you invoke Paralle.Foreach loop, the framework uses the threadpool thread. ThreadPool threads are chosen randomly and the executing code should never make any assumption on the identity of the chosen thread. In the original code its very much possible that dispatcher thread invoked via Parallel.Foreach loop is not able to figure out the thread which it is associated with. When you use explicit thread, then it works fine because the explicit thread has its own identity which can be relied upon by the executing code.
Ideally if your main concern is all about keeping UI responsive, then you should first use the Dispatcher class to push the code in background thread and then in there use what ever logic you want to speedup the overall execution.
if you want to use parallel foreach in GUI control like button click etc
then put parallel foreach in Task.Factory.StartNew
like
private void start_Click(object sender, EventArgs e)
{
await Task.Factory.StartNew(() =>
Parallel.ForEach(YourArrayList, (ArraySingleValue) =>
{
Console.WriteLine("your background process code goes here for:"+ArraySingleValue);
})
);
}//func end
it will resolve freeze/stuck or hang issue

Dispatcher.Invoke from a new thread is locking my UI

i'm using wpf, there's a button on my ui.
when the user clicks it, i have a for loop that runs a new method, on a new thread using autoresetevent.
in that method on that new thread, i'm using a label, let's call it lblStatus. i want to update that label on this thread that's not on the ui. using wpf, i have to use Dispatcher.Invoke.
here's a sample of my code:
Thread thread= new Thread(StartLooking);
thread.Start();
_waitHandle.WaitOne();
private void StartLooking(object value)
{
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
{
lblStatus.Content = "Scanning>...";
}
else
{
lblStatus.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => lblStatus.Content = "Scanning>>>>>"));
}
_waitHandle.Set();
}
the program just stops here. it doesn't change the content of the label, it returns to my ui, but blocks it.
i've tried
lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");
as well, but that isn't working also. any ideas?
The problem is that you're making it impossible for this to execute, since you're using Invoke.
Dispatcher.Invoke will not return until the UI thread processes. However, you've blocked the UI thread by calling _waitHandle.WaitOne();, and don't set the wait handle until AFTER this processes. The two effectively cause a dead lock.
If you switch this to use BeginInvoke instead, the UI will queue the element, the wait handle will set, THEN the label will update. It will work and not block, however.
Since the two previous posts already cover the problem in your code, just a suggestion: instead of
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
try using
if (!lblStatus.CheckAccess())
It's cleaner and has the exact intent you want. Just read about it here.
You probably want to use BeginInvoke instead. Invoke will block the thread that called it until the UI thread has run the Action, and since you're setting the priority to Background, this could take some time.
Best solution I have found for .net 4.5+ is using SynchronizationContext Post
Example (Task.Run's can be as many as you want in parallel accessing UI):
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var context = SynchronizationContext.Current;
Task.Run(() =>
{
var i = 0;
while (true)
{
context.Post((tmp) =>
{
uiText.Text = $"{i}";
}), this);
Thread.Sleep(1000);
i++;
}
});
}

BeginInvoke on ObservableCollection not immediate

In my code I subscribe to an event that happens on a different thread. Every time this event happens, I receive an string that is posted to the observable collection:
Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher;
var SerialLog = new ObservableCollection<string>();
private void hitStation_RawCommandSent(object sender, StringEventArgs e)
{
string command = e.Value.Replace("\r\n", "");
Action dispatchAction = () => SerialLog.Add(command);
currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Render);
}
The code below is in my view model (could be in the code behind, it doesn't matter in this case). When I call "hitstation.PrepareHit", the event above gets called a couple times, then I wait and call "hitStation.HitBall", and the event above gets called a couple more times.
private void HitBall()
{
try
{
try
{
Mouse.OverrideCursor = Cursors.Wait;
//prepare hit
hitStation.PrepareHit(hitSpeed);
Thread.Wait(1000);
PlayWarning();
//hit
hitStation.HitBall(hitSpeed);
}
catch (TimeoutException ex)
{
MessageBox.Show("Timeout hitting ball: " + ex.Message);
}
}
finally
{
Mouse.OverrideCursor = null;
}
}
The problem I'm having is that the ListBox that is bound to my SerialLog gets updated only when the HitBall method finishes. I was expecting seeing a bunch of updates from the PrepareHit, a pause and then a bunch more updates from the HitBall.
I've tried a couple of DispatcherPriority arguments, but they don't seem to have any effect.
I think you are blocking yourself.
The UI Thread is waiting at Thread.Wait, BeginInvoke sends the action to the dipatcher, however the UI Thread is busy waiting. That is why UI updates only get done after HitBall finishes, that is, when the UI Thread finishes processing.
To get around this, you should start the HitBall method's code in another thread, freeing the UI:
private void HitBall()
{
try {
Mouse.OverrideCursor = Cursors.Wait;
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
Action hitOperations = () =>
{
hitStation.PrepareHit(hitSpeed);
Thread.Wait(1000);
//Only needed if PlayWarning needs access to UI Thread
dispatcher.Invoke(() => PlayWarning());
hitStation.HitBall(hitSpeed);
dispatcher.Invoke(() => Mouse.OverrideCursor = null);
};
//Free the UI from work, start operations in a background thread
hitOperations.BeginInvoke(null, null);
}
catch (TimeoutException ex)
{
MessageBox.Show("Timeout hitting ball: " + ex.Message);
}
}
Also if the intended use of Thread.Wait(1000) was to wait for events to refresh the UI, with this implementation it is no longer needed.
Could it be something as simple as needing to raise the PropertyChanged event on your ViewModel?
You will probably not see updates from PrepareHit unless there is a context switch and there is no guarantee when context switch will occur (unless you block your current thread and that would increase the probability that a context switch will occur).
As iCe mentioned if you're doing this on the UI thread, then you might be blocking your UI. Does your UI block/freeze when you call Thread.Wait? If it doesn't then proceed reading below:
Update:
I can't think of anything that would not compromise concurrency... without compromising concurrency you could try to increase the priority of BeginInvoke: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx
currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Send);
By the way, it looks like Render priority is lower than Normal priority:
Render The enumeration value is 7.
Operations processed at the same
priority as rendering.
DataBind The
enumeration value is 8. Operations are
processed at the same priority as data
binding.
Normal The enumeration value
is 9. Operations are processed at
normal priority. This is the typical
application priority.
Send The
enumeration value is 10. Operations
are processed before other
asynchronous operations. This is the
highest priority.

Categories

Resources