async eventhandler executed multiple times - c#

I have this eventhandler, that can get executed multiple times. When I let it finish the execution, everything works fine (typing only one character and wait until the result is computed).
But when I type normally, a deadlock occurs. At least I think it is a deadlock.
private async void tbInput_TextChanged(object sender, TextChangedEventArgs e)
{
resultStackPanel.Children.Clear();
List<Task<UIElement>> tasks = new List<Task<UIElement>>();
if (tbInput.Text != "")
{
foreach (IModule mod in Modules)
{
if (mod.IsApplicable(tbInput.Text))
tasks.Add(mod.CalculateOutcome(tbInput.Text));
}
while (tasks.Count > 0)
{
await Task.WhenAny(tasks);
foreach (Task<UIElement> resultTask in tasks)
{
if (resultTask.Status == TaskStatus.RanToCompletion)
{
if (resultTask.Result != null)
{
resultStackPanel.Children.Add(resultTask.Result);
}
tasks.Remove(resultTask);
break;
}
}
}
}
}
I am pretty sure it is because of this line and that I should cancel all tasks, but I dont know how, since CancellationToken is useless because the libraries that perform the heavy work dont support it:
await Task.WhenAny(tasks);

I can see a few problems with your code as-is. For one, you'll get an infinite loop if any task does not complete successfully. For another, your existing tasks will still run to completion and attempt to update your UI.
Since your libraries don't support CancellationToken, you can't actually cancel the operations (which is bad). But you can at least pretend to cancel them by allowing them to run to completion and then ignoring the result. You can use a technique I call asynchronous callback contexts for this.
It's easier to split logic like this off into another method, instead of using a continuation; something like this:
private object _callbackContext;
private async void tbInput_TextChanged(object sender, TextChangedEventArgs e)
{
_callbackContext = new object();
resultStackPanel.Children.Clear();
if (tbInput.Text == "")
return;
Modules.Where(mod => mod.IsApplicable(tbInput.Text))
.Select(mod => ApplyModuleAsync(mod));
}
private async Task ApplyModuleAsync(IModule module)
{
var myContext = _callbackContext;
var element = await module.CalculateOutcome(tbInput.Text);
if (myContext != _callbackContext || element == null)
return;
resultStackPanel.Children.Add(element);
}

You can just add a continuation to the async method you're calling and then just asynchronously wait for all of them to complete with Task.WhenAll:
foreach (IModule mod in Modules)
{
if (mod.IsApplicable(tbInput.Text))
{
tasks.Add(mod.CalculateOutcome(tbInput.Text).ContinueWith(resultTask =>
{
if (resultTask.Result != null)
{
resultStackPanel.Children.Add(resultTask.Result);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion));
}
}
await Task.WhenAll(tasks.ToArray());

Related

Do multiple awaits to the same Task from a single thread resume in FIFO order?

Supposing a Task is created and awaited multiple times from a single thread. Is the resume order FIFO?
Simplistic example: Is the Debug.Assert() really an invariant?
Task _longRunningTask;
async void ButtonStartSomething_Click()
{
// Wait for any previous runs to complete before starting the next
if (_longRunningTask != null) await _longRunningTask;
// Check our invariant
Debug.Assert(_longRunningTask == null, "This assumes awaits resume in FIFO order");
// Initialize
_longRunningTask = Task.Delay(10000);
// Yield and wait for completion
await _longRunningTask;
// Clean up
_longRunningTask = null;
}
Initialize and Clean up are kept to a bare minimum for the sake of simplicity, but the general idea is that the previous Clean up MUST be complete before the next Initialize runs.
The short answer is: no, it's not guaranteed.
Furthermore, you should not use ContinueWith; among other problems, it has a confusing default scheduler (more details on my blog). You should use await instead:
private async void ButtonStartSomething_Click()
{
// Wait for any previous runs to complete before starting the next
if (_longRunningTask != null) await _longRunningTask;
_longRunningTask = LongRunningTaskAsync();
await _longRunningTask;
}
private async Task LongRunningTaskAsync()
{
// Initialize
await Task.Delay(10000);
// Clean up
_longRunningTask = null;
}
Note that this could still have "interesting" semantics if the button can be clicked many times while the tasks are still running.
The standard way to prevent the multiple-execution problem for UI applications is to disable the button:
private async void ButtonStartSomething_Click()
{
ButtonStartSomething.Enabled = false;
await LongRunningTaskAsync();
ButtonStartSomething.Enabled = true;
}
private async Task LongRunningTaskAsync()
{
// Initialize
await Task.Delay(10000);
// Clean up
}
This forces your users into a one-operation-at-a-time queue.
The order of execution is pre-defined, however there is potential race condition on _longRunningTask variable if ButtonStartSomething_Click() is called concurrently from more than one thread (not likely the case).
Alternatively, you can explicitly schedule tasks using a queue. As a bonus a work can be scheduled from non-async methods, too:
void ButtonStartSomething_Click()
{
_scheduler.Add(async() =>
{
// Do something
await Task.Delay(10000);
// Do something else
});
}
Scheduler _scheduler;
class Scheduler
{
public Scheduler()
{
_queue = new ConcurrentQueue<Func<Task>>();
_state = STATE_IDLE;
}
public void Add(Func<Task> func)
{
_queue.Enqueue(func);
ScheduleIfNeeded();
}
public Task Completion
{
get
{
var t = _messageLoopTask;
if (t != null)
{
return t;
}
else
{
return Task.FromResult<bool>(true);
}
}
}
void ScheduleIfNeeded()
{
if (_queue.IsEmpty)
{
return;
}
if (Interlocked.CompareExchange(ref _state, STATE_RUNNING, STATE_IDLE) == STATE_IDLE)
{
_messageLoopTask = Task.Run(new Func<Task>(RunMessageLoop));
}
}
async Task RunMessageLoop()
{
Func<Task> item;
while (_queue.TryDequeue(out item))
{
await item();
}
var oldState = Interlocked.Exchange(ref _state, STATE_IDLE);
System.Diagnostics.Debug.Assert(oldState == STATE_RUNNING);
if (!_queue.IsEmpty)
{
ScheduleIfNeeded();
}
}
volatile Task _messageLoopTask;
ConcurrentQueue<Func<Task>> _queue;
static int _state;
const int STATE_IDLE = 0;
const int STATE_RUNNING = 1;
}
Found the answer under Task.ContinueWith(). It appear to be: no
Presuming await is just Task.ContinueWith() under the hood, there's documentation for TaskContinuationOptions.PreferFairness that reads:
A hint to a TaskScheduler to schedule task in the order in which they were scheduled, so that tasks scheduled sooner are more likely to run sooner, and tasks scheduled later are more likely to run later.
(bold-facing added)
This suggests there's no guarantee of any sorts, inherent or otherwise.
Correct ways to do this
For the sake of someone like me (OP), here's a look at the more correct ways to do this.
Based on Stephen Cleary's answer:
private async void ButtonStartSomething_Click()
{
// Wait for any previous runs to complete before starting the next
if (_longRunningTask != null) await _longRunningTask;
// Initialize
_longRunningTask = ((Func<Task>)(async () =>
{
await Task.Delay(10);
// Clean up
_longRunningTask = null;
}))();
// Yield and wait for completion
await _longRunningTask;
}
Suggested by Raymond Chen's comment:
private async void ButtonStartSomething_Click()
{
// Wait for any previous runs to complete before starting the next
if (_longRunningTask != null) await _longRunningTask;
// Initialize
_longRunningTask = Task.Delay(10000)
.ContinueWith(task =>
{
// Clean up
_longRunningTask = null;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
// Yield and wait for completion
await _longRunningTask;
}
Suggested by Kirill Shlenskiy's comment:
readonly SemaphoreSlim _taskSemaphore = new SemaphoreSlim(1);
async void ButtonStartSomething_Click()
{
// Wait for any previous runs to complete before starting the next
await _taskSemaphore.WaitAsync();
try
{
// Do some initialization here
// Yield and wait for completion
await Task.Delay(10000);
// Do any clean up here
}
finally
{
_taskSemaphore.Release();
}
}
(Please -1 or comment if I've messed something up in either.)
Handling exceptions
Using continuations made me realize one thing: awaiting at multiple places gets complicated really quickly if _longRunningTask can throw exceptions.
If I'm going to use continuations, it looks like I need to top it off by handling all exceptions within the continuation as well.
i.e.
_longRunningTask = Task.Delay(10000)
.ContinueWith(task =>
{
// Clean up
_longRunningTask = null;
}, TaskContinuationOptions.OnlyOnRanToCompletion);
.ContinueWith(task =>
{
// Consume or handle exceptions here
}, TaskContinuationOptions.OnlyOnFaulted);
// Yield and wait for completion
await _longRunningTask;
If I use a SemaphoreSlim, I can do the same thing in the try-catch, and have the added option of bubbling exceptions directly out of ButtonStartSomething_Click.

Using Dispatcher correctly in event to update UI

I'm using dispatchers to update a bound collection from an event. I just ran into a nasty issue where I had two different dispatchers in the same event and it wasn't working. Using the debugger it was completely skipping over the code in the first dispatcher. Putting the entire event in a single dispatcher fixed it. I assume it's because of how the compiler handles it, can anyone confirm this - only one dispatcher per event, at least when dealing with the same elements?
Here is the code, when it gets to the await after (line == 0), it exits the function completely. Later, when line !=0 it runs the "Old style menu" fine. If I put all of the code in a single dispatcher, everything works fine.
private async void ProcessNLS(string parameters) // NET/USB List Info
{
if (parameters.Substring(0, 1) == "A" || (parameters.Substring(0, 1) == "U")) // ASCII x08/2010 Only
{
int line = Convert.ToInt32(parameters.Substring(1, 1));
string text = parameters.Substring(3);
// New Menu, Clear Old - Use Last Received/Holding Menu: See NLT bug
if (line == 0)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
State.Menu.ServiceType = State.holdingMenu.ServiceType;
...
State.Menu.Items.Clear();
});
OnMenuTitleInfoChanged(new MenuTitleInfoChangedArgs(State.Menu));
// Replace Network Top with custom menu
if (State.Menu.LayerInfo == LayerTypes.NetworkTop)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
State.Menu.Items.Clear();
});
...
}
// Get 1st Advanced Menu
if (Device.SupportsAdvancedMenus & State.Menu.LayerInfo != LayerTypes.NetworkTop)
{
...
}
}
// Old style menu
if (!Device.SupportsAdvancedMenus && State.Menu.LayerInfo != LayerTypes.NetworkTop)
{
NetworkMenuItem menuItem = new NetworkMenuItem(line, text);
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
State.Menu.Items.Add(menuItem);
});
OnMenuLoading(new MenuLoadingArgs(menuItem));
}
}
// C - Track Cursor
if (parameters.Substring(0,1) == "C")
{
if (parameters.Substring(1, 1)== "-")
{
// No Cursor
// Sent when entering player screen
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
...
State.Menu.Items.Clear();
OnMenuTitleInfoChanged(new MenuTitleInfoChangedArgs(State.Menu));
}
}
});
}
Like this it would just jump over the dispatcher for no apparent reason. If I put the entire thing in a single dispatcher it works fine.
A second question, if I have another event with a dispatcher, something like this:
foreach (xxx)
{
if (xxx == yyy)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
State.Menu.Items.Add(menuItem);
});
}
}
Would it be preferable to instead wrap the entire foreach loop in a dispatcher rather then calling it when needed each iteration?
Since my original question has changed I've made a new post with more specifics and another possible solution by just wrapping the original socket listener task in a dispatcher
Possible solution to issue with multiple UI dispatchers in the same method?
*** Update:
I think Raymond is on the right track, though adding Task didn't fix it, I noticed although it starts processing line "0" of the menu, before it sets up the new menu it tries to process the next line "1" command which is ignored because it doesn't have the right menu state yet, it still hasn't been set by the previous command yet.
I'm not sure how to fix it, it seems like I have to do an await at a lower level so be sure sure it full finishes one command before starting the next (and not sure why putting the whole ProcessNLS in UI dispatcher works), it's a little complicated since I go through multiple levels but here is the flow:
socket = new StreamSocket();
try
{
await socket.ConnectAsync(new HostName(HostName), Port);
OnConnect(new EventArgs());
await Task.Factory.StartNew(WaitForMessage);
}
catch (Exception ex)
{
OnConnectionFail(new EventArgs());
}
Goes to:
private async void WaitForMessage()
{
...
foreach (var message in messages)
{
if (string.IsNullOrWhiteSpace(message))
continue;
ProcessMessage(message);
}
}
Goes to
private void ProcessMessage(string message, string optionalFlags = "")
{
...
case "NLS": // NET/USB List Info
ProcessNLS(parameters);
break;
}
to finally
private async void ProcessNLS(string parameters) // NET/USB List Info
My alternate solution is to put to ProcessMessage call under WaitForMessage in a UI dispatcher
*** Update #2
I think this may be working, here is the updated flow, have to await multiple steps, use task instead of void
private async void WaitForMessage()
{
...
foreach (var message in messages)
{
if (string.IsNullOrWhiteSpace(message))
continue;
await ProcessMessage(message);
}
}
}
catch (Exception ex)
{
Debug.WriteLine("WaitForMessage Error: " + ex.Message);
OnDisconnect(new EventArgs());
}
}
to
private async Task ProcessMessage(string message, string optionalFlags = "")
{
...
case "NLS": // NET/USB List Info
await ProcessNLS(parameters);
break;
}
to
private async Task ProcessNLS(string parameters) // NET/USB List Info
The problem is here:
private async void ProcessNLS(...)
^^^^^^^^^^
You declared an async void function, which means "When the first await happens, return from the function immediately, and let the rest of the work run asynchronously." If you want the caller to be able to await on completion of your function, change the signature to private async Task ProcessNLS(...).

C# raise Event in Task.WhenAll()

I have a producer–consumer multiply task Class.
I have a method:
private async Task Consume(CancellationToken cancellationToken){..}
There is a starting method:
public void Run()
{
var workerCount = Session.GetParameters().GetThreadCount();
_workers = new List<Task>(workerCount);
for (var i = 0; i < workerCount; i++)
_workers.Add(Consume(StopCancellationTokenSource.Token));
Task.WhenAll(_workers).ContinueWith(_ => DoneEvent);
}
The problem is that DoneEvent is fired, but subscriber event handler never get executed.
The only way that I succeeded to run event handler is where I made the Run method async and added await before Task.WhenAll(..). But then another issue raised. In the method Consume() I have a ManualResetEvent PauseBlock. And when it is reset the main thread waits also.
Thnx in advance.
EDIT:
I have managed to do it right (after two days)
I have changed a little bit the Run method:
public async void Run()
{
var workerCount = Session.GetParameters().GetThreadCount();
_workers = new List<Task>(workerCount);
for (var i = 0; i < workerCount; i++)
_workers.Add(Task.Run(()=> Consume(StopCancellationTokenSource.Token)));
await Task.WhenAll(_workers);
DoneEvent();
}
Now it is working correctly.
Just for info Consumer method :
private async Task Consume(CancellationToken cancellationToken)
{
try
{
await Task.Delay(5000, cancellationToken);
IEngGroup engGroup;
while (Groups.TryDequeue(out engGroup))
{
cancellationToken.ThrowIfCancellationRequested();
if (!engGroup.IsEnabled || engGroup.Result.Status == ItemRunningStatus.Successful) continue;
if (engGroup.IsBreak) Pause();
//if paused wait
PauseBlock.WaitOne();
//if stoped throw
cancellationToken.ThrowIfCancellationRequested();
var groupRunner = new GroupRunner(cancellationToken, PauseBlock);
if (engGroup.FireAndForget)
groupRunner.RunGroup(engGroup);
else
await groupRunner.RunGroup(engGroup);
}
return;
}
catch (OperationCanceledException)
{
return ;
}
}
Thnx to all of you.
If you have any suggestion on ow to improve, I would like to see it.
Try this:
Run().ConfigureAwait(false);
public async Task Run()
{
...
await Task.WhenAll(_workers).ContinueWith(_ => DoneEvent);
}
So this will tell the run it doesn't need to return to the current thread again and finish executing on what ever thread it went onto. It won't block the main thread.
But I must warn you, any code ran on the "DoneEvent" will be executed on the other thread, so if you have any code which affects, for example the UI Thread, you will get a Invalid cross-thread access exception.

Take the Last Requested Async Result and Cancelling Previous Unfinished Tasks

I have multiple heavy job calculation requests. The job may take different time. By using async and await I want to take the last requested result with canceling eventually unfinished previous tasks.
Currently I'm using BackGroundWorker with setting a job ID. I used only the the result with the last requested ID.
Can I rewrite the code with using async await?
private int backtestId;
private void PrepareStrategyCalculation()
{
backtestId = backtestManager.GetNextBacktestId();
strategy.BacktestId = backtestId;
backtestManager.StartBacktestWorker(strategy.Clone());
}
private void BacktestManager_StrategyBacktested(object sender, StrategyBacktestEventArgs e)
{
if (e.BacktestObject.Strategy.BacktestId != backtestId) return;
var calculatedStrategy = e.BacktestObject.Strategy;
...
}
EDIT:
Is this a solution?
private int backtestId;
private async void PrepareStrategyCalculation()
{
backtestId = backtestManager.GetNextBacktestId();
strategy.BacktestId = backtestId;
var calculatedStrategy = await backtestManager.StartBacktestAsync(strategy.Clone());
if (calculatedStrategy.BacktestId != backtestId) return;
...
}
Assuming your code is CPU-bound, then Task.Run is a suitable substitute for BackgroundWorker.
You can use CancellationTokenSource to cancel tasks. So, something like this would work, assuming that StartBacktestAsync is called from a single-threaded context such as a UI thread:
private CancellationTokenSource _cts;
async Task StartBacktestAsync()
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
try
{
var token = _cts.Token;
await Task.Run(() => Backtest(token));
}
catch (OperationCanceledException)
{
// Any special logic for a canceled operation.
}
}
void Backtest(CancellationToken token)
{
... // periodically call token.ThrowIfCancellationRequested();
}

Comparence of task.Status == RanToCompletion and task.IsCompleted

Consider this:
readonly INotifyTaskCompletion<Model> _modelAsync;
public INotifyTaskCompletion<Model> ModelAsync { get { return _modelAsync; } }
public ctor() // pseudo code
{
_modelAsync = NotifyTaskCompletion.Create(GetModelAsync());
_modelAsync.PropertyChanged += modelAsync_Ready;
}
async Task<Model> GetModelAsync()
{
var rv = await TaskEx.Run(new Func<Model>(() => Agent.GetModel());
if (ModelAsync.IsSuccessfullyCompleted) Trace.WriteLine("after await completed");
if (ModelAsync.Status != TaskStatus.RanToCompletion) Trace.WriteLine("after await not completed");
if (ModelAsync.Result != null) Trace.WriteLine("after await result");
if (ModelAsync.Result == null) Trace.WriteLine("after await no result");
return rv;
}
void modelAsync_Ready(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Result") { }// etc...
}
Why is it that after the await the task has not yet completed?
I always thought that the big plus of async-await pattern was: not having to write a callback any more! Async programming with the look and feel of a "normal program".
But only by subscribing to the propertyChanged(this, new PropertyChangedEventArgs("Result")); I get notified that the Result is there.
That is approx. the same amount of work as writing a callback or backgroundworker.
You're awaiting the task returned from TaskEx.Run, but you're not setting ModelAsync as far as I can see.
Assuming that ModelAsync is Task<Model>, then perhaps you meant:
async Task<Model> GetModelAsync()
{
ModelAsync = TaskEx.Run(...);
await ModelAsync;
// ModelAsync.IsCompleted is true here.
}
Note that Task and Task<T> do not implement INotifyPropertyChanged. If you're wanting to do data binding that updates when a task completes, then check out my NotifyTaskCompletion type in the AsyncEx library.

Categories

Resources