Processing with Stemmer.Cvb.Async
(Windows Forms)
In the last post we have seen how to do image processing with the StreamHandler
:
And there I also stated, that if you want to have a fluent UI, you should use the Stemmer.Cvb.Async
functionality.
Disclaimer: this quickly becomes an advanced topic if you do parallel processing!
You have been warned… We integrate nicely with the asynchronous and parallel processing capabilities of .Net.
Stream is an IObservable
One way we have seen above with Rx:
:cvb: Stream
s implement the IObservable<T>
interface which enables you to .Subscribe
to the Stream
. The Stream
pushes the acquired Image
s from a long-running Task
to your registered IObserver<T>
(where T
is StreamImage
). The StreamHandler
utilizes this, in fact. It adds to that implementation the synchronzation to the UI thread-context to make your life easier. But if you want to do processing outside the UI thread-context, the IObservable<T>.Subscribe
is a good place to start.
Stemmer.Cvb.Async
Acquisition
In this post I describe a way that is more like the StreamHandler
(it originates from the UI thread-context), but is more flexible. To make this work you first need to add these two namespace:
using Stemmer.Cvb.Async;
using Stemmer.Cvb.Driver;
The first one is for the async
extension methods an the second one for the StreamImage
which is returned by the .Wait
method. For this to work you only need to rewrite your grabCheckBox_CheckedChanged
event handler like this:
private async void grabCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (grabCheckBox.Checked)
{
Device.Stream.Start();
openButton.Enabled = false;
try
{
while (grabCheckBox.Checked)
{
using (StreamImage image = await Device.Stream.WaitAsync())
{
// processing
}
}
}
catch (OperationCanceledException)
{
// acquisition was aborted
}
finally
{
openButton.Enabled = true;
}
}
else
{
if (Device.Stream.IsRunning)
Device.Stream.Abort();
}
}
Note the async
keywork added in the method signature and the
await Device.Stream.WaitAsync()
call. Windows Forms is async
/await
capable so that if you check the grabCheckBox
this method enters the then-branch of the if
-conditional. It all runs in the UI thread-context. All these calls are reasonable fast, so that you won’t experience any lags. Until you reach the .Wait
. This is now an awaitable .WaitAsync
. The await
cooperatively waits for the operation being finished and continues on after wards. While waiting, though, it gives control back to the UI so it stays reactive.
If you uncheck the grabCheckBox
this method is reentered(!), but now goes to the else branch which .Abort
s the acquisition. When there is a free slot in Windows Forms message queue handling, the still running grabCheckBox_CheckedChanged
then-branch is ‘unthawed’ and also exits (as .Checked
is now false
). Also, dependent on when the .Abort()
was issued, the .Wait
may have been canceled (resulting in the OperationCanceledException
).
Ok, why use this instead of the events? Because you are more flexible. You could
- use
WaitFor
if you expect images to arrive in a certain time-frame - wait on two or more cameras simultanously
- have a driver that does not support
.Abort()
- …
Exit Handling
Exit handling should always be a priority if you do async
or even parallel processing (e.g. via Task.Run
). As long as you only perform short tasks in the UI thread, your are fine if you call
grabCheckBox.Checked = false;
in the void Dispose(bool disposing)
method if disposing
is true
.
If you do your own parallel processing, then you must wait until that is finished in exit scenarios. I recommend creating a TaskCompletionSource<bool>
(it has to have some type) like this (this code is inserted where the comment // Processing...
is placed:
var tsc = new TaskCompletionSource<bool>();
_processingFinished = tsc.Task; // _processingFinished is of type Task
await Task.Run(() =>
{
// lengthy processing
// when finished:
tsc.TrySetResult(true);
});
And then register the FormClosing event:
if (grabCheckBox.Checked)
{
grabCheckBox.Checked = false;
await _processingFinished;
}
This is safe as the TaskCompletionSource
is created in the UI thread-context and the FormClosing event is also executed in the UI thread-context.