So I'm calling Dispatcher.BeginInvoke() to perform some UI actions in a Timer.Elapsed event. The timer is ticking fast, and multiple new instances of BeginInvoke() may stack up before a previous call is fully processed. After processing of current call is finished, I'm always interested in picking the latest instance of BeginInvoke() only, i.e. any previous unprocessed instances on the message queue should be discarded.
What is the correct way of emptying the Dispatcher's BeginInvoke queue to achieve this?
To demonstrate an example, consider that I'm reading value from a sensor in Timer.Elapsed event several times a second and then updating a complex UI to show the read values. This UI update action takes some time and during this time, one or more new instances of the read values stack up on the dispatcher queue to get rendered. Obviously, when I have got more recent values from the sensor, I'd want to discard all instances in the waiting line and just keep the current one, to be sent for rendering once the processor is free.
There is no chance to dequeue callbacks since you are not managing the UI thread.
But you could use a CancellationTokenSource:
Pass the CancellationTokenSource (CancellationTokenSource.Token)
to the dispatcher and your callback.
Listen for cancellation by repeatedly invoking CancellationToken.ThrowIfCancellationRequested() inside your callback and catch the OperationCanceledException exception that will be thrown once the CancellationTokenSource.Cancel() was called
Use a catch block to catch OperationCanceledException and do the clean up in order to reverse state to prior of executing the callback
Before invoking the dispatcher with a new action you cancel all previous callbacks by invoking CancellationTokenSource.Cancel(). This will trigger the ThrowIfCancellationRequested() to actually throw an OperationCanceledException inside the callback.
Invoke dispatcher with new callback and new CancellationToken from a fresh CancellationTokenSource instance and dispose all cancelled CancellationTokenSource instances.
This way you can cancel the dispatcher action e.g. in case it's long running or prevent it to be executed in case the action is still pending. Otherwise you have to enqueue the next dispatcher action and override the changes of the previous action.
Dispatcher.InvokeAsync(...) is equal to Dispatchher.BeginInvoke(...) but in addition it allows you to pass a cancellation token to the dispatcher.
Related
I've read about asynchronous programming in C#, but I still do not fully understand how continuation of async method is executed.
From my understanding, Asynchronous programming is not about multithreading. We can run async method on UI Thread, and it later continues on that UI Thread (while not blocking and continuing to respond to other messages from message loop).
This is basic message loop most GUI apps have:
while (1)
{
bRet = GetMessage(&msg, NULL, 0, 0);
if (bRet > 0) // (bRet > 0 indicates a message that must be processed.)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
...
DispatchMessage() calls UI event handlers. And the code inside the event handlers should not block the main thread. For that reason if we want to, i.e. create a button that loads a heavy data from disk, we can use async method like this: (simplified pseudo code)
public async Task ButtonClicked()
{
loadingBar.Show();
await AsyncLoadData();
loadingBar.Hide();
}
When execution reaches await AsyncLoadData(); line, it stores the context and returns the Task object. DispatchMessage() finishes and message loop repeatedly comes to the bRet = GetMessage(&msg, NULL, 0, 0); line.
So my question is, how the rest code is executed? Is finished async operation triggers a new message, that is then handled by DispatchMessage() again? Or the message loop has another method (after dispatch), that checks for finished async operations?
So my question is, how the rest code is executed? Is finished async operation triggers a new message, that is then handled by DispatchMessage() again? Or the message loop has another method (after dispatch), that checks for finished async operations?
await by default will capture a "context" and use that to resume the execution of the method. This "context" is SynchronizationContext.Current, falling back on TaskScheduler.Current. UI apps provide a SynchronizationContext, e.g., WindowsFormsSynchronizationContext or DispatcherSynchronizationContext. When the await completes, it schedules the continuation of the method onto that context (in this case, onto the SynchronizationContext).
For WinForms, the syncctx uses Control.BeginInvoke, which will post a Win32 message which is handled by the WinProc.
For WPF, the syncctx posts to its Dispatcher, which adds the callback to the dispatch queue. This queue is also processed by a Win32 WinProc loop.
Alex Davies wrote an excellent book on this, it is called "Async in C# 5", I strongly recommend reading it.
I can't tell low level details behind this, but at the high level CLR will create something like this:
void __buttoncliked_remaining_code_1(...) {
loadingBar.Hide();
}
So, a specific event will be triggered indicating that async job has completed.
Then __buttoncliked_remaining_code_1() will be executed, sinchronously, just like any regular C# function.
CLR will use for that any thread, but most likely it will reuse the one that encountered await keyword, which in your case might be GUI thread.
I have an async Method named "RequestMessage()". Within this method, I'm going to send a message to a message broker. Since I don't know when to expect the result, I'm using "TaskCompletionSource". I want the async method to terminate, when the reply message arrived (I'll receive an event from the broker).
This works fine so far. Now, my issue is that this message could never be answered, or at least way to late.
I'm looking for a change to implement my own timeout. To do so, I tried a Timer, as well as the Observer of Reactive Extensions. Th issue is always the same - I can't get my main thread and the timer thread syncronized, as I'm using .NET Core 2 and there is no SynchronizationContext.
So, in my code there is an observer ..
Observable
.Interval(TimeSpan.FromSeconds(timeOutInSeconds))
.Subscribe(
x =>
{
timeoutCallback();
});
If time expires a callback should be called. In my caller method, I handle the callback this way:
TimeoutDelegate timeoutHandler = () => throw new WorkcenterRepositoryCommunicationException("Broker communication timed out.", null);
As you realized already, this Exception will never be catched, as it is not thrown to the main thread.
How can I sync threads here?
Thanks in advance!
The best way to "fail upon some problem" IMHO would be to throw the appropriate exception, but you can definitely just use return; if you prefer to avoid exceptions.
This will create a completed/faulted task that was completed synchronously, so the caller using await will get a finished task and continue on using the same thread.
CancellationToken allows for the caller to cancel the operation, which isn't the case you are describing.
Task.Yield doesn't terminate any operation, it just enables other tasks to run for some time and reschedules itself for later.
I assume that I can just call
var dispatcherOp = Application.Current.Dispatcher.BeginInvoke(Action());
dispatcherOp.Completed += dispatcherOp_Completed;
but then I am a little concerned. If begininvoke is asynchronous, then What prevents the dispatcher from completing the action before I get the returned dispatcher operation and attach to the completed event.
i assume that this must be impossible, but if so, I would like to know why.
You are correct; this is not safe. You should simply await the operation.
The answer is: nothing.
There are three possible approaches:
1.) You can decide that it is not important to wait for the operation to be executed. If so, then you are choosing the fire and forget strategy.
2.) You can decide that you want a synchronous request, so you await, like SLaks suggested.
3.) You can keep the request to be asynchronous, but use a callback.
Unfortunately Dispatcher does not have a callback function it takes for begininvoke.
Synchronous operation is not desired in my program, but I realized after a google search or two that I can lock on it like this
lock (this)
{
var dispatcherOp = Application.Current.Dispatcher.BeginInvoke(MyAction, DispatcherPriority.Normal);
dispatcherOp.Completed += dispatcherOp_Completed;
}
void dispatcherOp_Completed(object sender, EventArgs e)
{
lock (this)
{
}
}
and then lock again on the same object in the completed function so that the dispatcher can't complete till you have finished attaching your handler.
There is never a reason to use Dispatcher.BeginInvoke in a modern application.
If your code models a background operation, then use async/await, with IProgress<T> for any progress updates.
If your code models an asynchronous sequence, then use Rx (Reactive Extensions) with ObserveOn to update objects with UI thread affinity.
I have a thread that grabs messages from a concurrent queue and writes them to a network stream. The loop inside the thread looks like this:
while (!cancel.IsCancellationRequested)
{
messageBus.outboundPending.WaitOne();
var message = messageBus.GetFrom(Direction.Outbound);
if (message == null) { continue; }
MessageWriter.WriteMessage(networkStream, message, cancel, OnStreamClose).Wait(cancel);
}
The requirement is that the thread stops if the cancellation token is set. However, since it waits for pending messages in the queue, the thread will stay blocked. How could I "combine" both the cancellation token and the outbound event so that if either of them are set, the thread unblocks?
The only convoluted way that I can think of to make it work is to replace the outboundPending event with a new third event, and start two new threads: one that waits for the outbound event, and another that waits for the cancel event, and have both of them set the third event when they unblock. But that feels really bad.
Try WaitHandle.WaitAny and include the CancellationToken.WaitHandle.
A discussion of a cancellable WaitAll can be found here
Use the WaitOne(TimeSpan) method. It will return true if it was signaled, and false if the timeout was reached.
e.g, if you send TimeSpan.FromSeconds(1) and a second has passed without a signal, the execution will continue and the method will return false. If the signal was given, it will return true.
I am planning to use Auto reset Event Handle for Inter Thread communication.
EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.AutoReset);
My producer thread code look like below
produceSomething();
handle.Set();
In the consumer thread, I have to download data for every one minute or when producer
is called Set method
try
{
while(true)
{
handle.WaitOne(60000, false);
doSomething(); // Downloads data from Internet.
// Takes lot of time to complete it.
}
}
catch(ThreadAbortException)
{
cleanup();
}
My question is if consumer thread is running doSomething function and producer calls set function, what would be state of Auto reset event object?
My requirement is as soon as producer calls set method I have to download fresh data from the Internet. If doSomething function is running, when Producer calls set method, I have to interrupt it and call again.
An auto-reset event is like a gate that closes after the first thread goes through. If you set it while one or more threads are waiting then One thread wakes up, then the event is reset, the rest of the threads continue to wait.
If you set when no threads are waiting, then the first thread that calls handle.WaitOne will not wait, but it will cause the event to get reset and then continue on.
from http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx
Calling Set signals AutoResetEvent to release a waiting thread. AutoResetEvent remains signaled until a single waiting thread is released, and then automatically returns to the non-signaled state. If no threads are waiting, the state remains signaled indefinitely.
If a thread calls WaitOne while the AutoResetEvent is in the signaled state, the thread does not block. The AutoResetEvent releases the thread immediately and returns to the non-signaled state.
The problem with auto-reset event in your scenario is that "setting" it do not supports queuing.
That is, setting an auto-reset event allows one thread to enter, if you set it again before any thread "consumes" your event, then that "set" will be lost. You might expect for two threads to be able to enter and consume whatever you have produced but in fact only ONE thread will be able to do that.
In your case, if you're producing at a faster rate than you're consuming then the auto-reset event might be missleading. Imagine this case.
The producer produces one item.
The consumer consumes the item (resets the event and starts downloading from inet)
The producer produces a second item.
The producer produces a third item.
The produce stops.
The consumer consumes the second item (resests the event and starts downloading again)
The consumer WON'T cosume the third item ever because the autoreset event has been reset.