Comparence of task.Status == RanToCompletion and task.IsCompleted - c#

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.

Related

Mixing async-await with fire-and-forget approach

I'm writing a websocket server using .NET's HttpListener class.
Essentially, I've got a HandleListener() function which wait for clients to connect and yield each client to HandleClient(WebSocket client). So I currently have:
private async void HandleListener()
{
try
{
while (listener != null && listener.IsListening)
{
HttpListenerContext listenerContext = await listener.GetContextAsync();
WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
WebSocket webSocket = webSocketContext.WebSocket;
clients.Add(webSocket);
await HandleClient(webSocket);
}
}
catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
}
private async Task HandleClient(WebSocket client) { ... }
Problem is, I can't seem to process more then one client. It looks like the execution of HandleListener() halts as long as the first client is connected.
I tried removing the await from the call to HandleClient(), but I get the "because this call is not awaited..." error. I can make HandleClient() a async void method, but this is not an event handler.
BTW, the reason that HandleClient() is async Task is because it's doing, all over in a loop until the listener is dead:
recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);
From what I understand, a fire-and-forget approach is bad overall, and I can't seem to achieve it with async-await implementation. But HandleClient() is a fire-and-forget method, and I don't see any other way of achieving what I need.
EDIT: Added current implementation of HandleClient():
private async Task HandleClient(WebSocket client)
{
try
{
ArraySegment<byte> recievedBuffer = new ArraySegment<byte>(new byte[BUFFER_SIZE]);
while (listener != null && listener.IsListening && client.State == WebSocketState.Open)
{
WebSocketReceiveResult recieveResult;
using (var ms = new MemoryStream())
{
do
{
recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);
ms.Write(recievedBuffer.Array, recievedBuffer.Offset, recieveResult.Count);
}
while (!recieveResult.EndOfMessage);
switch (recieveResult.MessageType)
{
case WebSocketMessageType.Close:
RemoveClient(client, WebSocketCloseStatus.NormalClosure, string.Empty);
break;
case WebSocketMessageType.Binary:
RemoveClient(client, WebSocketCloseStatus.InvalidMessageType, "Cannot accept binary frame");
break;
case WebSocketMessageType.Text:
OnRecieve?.Invoke(client, System.Text.Encoding.UTF8.GetString(ms.ToArray()));
break;
}
}
}
}
catch (WebSocketException ex)
{
RemoveClient(client, WebSocketCloseStatus.InternalServerError, ex.Message);
}
}
To prevent compiler warning, use method like this:
public static class TaskExtensions {
public static void Forget(this Task task) {
}
}
then just do
HandleClient(webSocket).Forget()
If you go this route, ensure that you handle all exceptions inside HandleClient somehow (wrap whole thing into try-catch for example). There is nothing inherently "bad" in this approach in this particular case.
Alternative approach would be:
HandleClient(webSocket).ContinueWith(task => {
if (task.IsFaulted && task.Exception != null) {
// handle it here
}
});
awaiting HandleClient is not an option in this case, as you see yourself.
it will do like that because you wrote code for it, my mean to say you wrote method as below.
private async void HandleListener()
{
try
{
while (listener != null && listener.IsListening)
{
HttpListenerContext listenerContext = await listener.GetContextAsync();
WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
WebSocket webSocket = webSocketContext.WebSocket;
clients.Add(webSocket);
await HandleClient(webSocket);
}
}
catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
}
In this method when it encounter await control will get return to orignal caller ,till you await part got completed and next call start after it.
Check below image this how await and async works
If you just want fire and forget than try like this
private void HandleListener()
{
try
{
while (listener != null && listener.IsListening)
{
HttpListenerContext listenerContext = await listener.GetContextAsync();
WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
WebSocket webSocket = webSocketContext.WebSocket;
clients.Add(webSocket);
HandleClient(webSocket);
}
}
catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
}
which means dont wait for completion of task

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.

Task.WhenAny ContinueWith: Get argument?

I want to execute a list of tasks, and perform a synchronous action once any of them is completed, but I need to know which of them it was.
See my example, and look out for the comment in the code, that precedes a couple of lines I don't know how to achieve.
public async Task<bool> GreetAsync(string name)
{
if (name == null)
return false;
await InternalGreeter.GreetAsync(name);
return true;
}
public async Task GreetAllAsync()
{
var tasks = UserNames.Select(un => GreetAsync(un)).ToList();
while(tasks.Any())
{
var finished = await Task.WhenAny(tasks);
if(finished.Result)
{
//Here's what I'd like to achieve
var username = finished.Arguments[0];
WriteLine($"User {username} has been greeted.");
}
tasks.Remove(finished);
}
}
Based on this example.
In my real world scenario, I have a list of customers, which I have to walk thru them one by one and update a remote server on their credit status (the remote server doesn't support batch updates). After each of them has been updated, I have to mark in my database, that this customer has been accredited.
You almost never want to actually process a list of tasks one at a time as they complete like that. Instead, just introduce a higher-level operation and rewrite your Task.WhenAny to a Task.WhenAll to wait for those higher-level operations.
public async Task<bool> GreetAsync(string name)
{
if (name == null)
return false;
await InternalGreeter.GreetAsync(name);
return true;
}
private async Task<bool> GreetAndReportGreetedAsync(string name)
{
var result = await GreetAsync(name);
WriteLine($"User {name} has been greeted.");
return result;
}
public async Task GreetAllAsync()
{
await Task.WhenAll(UserNames.Select(un => GreetAsync(un));
}
Why not simply use ContinueWith? Something like this:
public async Task GreetAllAsync(List<string> UserNames)
{
var tasks = UserNames
.Select(un => GreetAsync(un)
.ContinueWith(x => {
Console.WriteLine(un + " has been greeted");
}));
await Task.WhenAll(tasks);
}

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();
}

async eventhandler executed multiple times

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());

Categories

Resources