Why does PauseToken break the task? - c#

I'm trying to understand Microsoft.VisualStudio.Workspace.PauseToken.
When I have called PauseTokenSource.Pause(), the method is paused and the task is completed, and I can see "Completed" message.
After that I have called PauseTokenSource.Resume(), and the task is continued.
But I have no message when the process has finished.
Two questions:
Why does this happen?
How can I fix it?
I want PauseToken to not break the task during the work. I don't want to get the message after pause. I want to get message at true end of process.
public PauseToken PauseToken;
private async Task DoDoneAsync()
{
FlagCommandDone = true;
try
{
task = Task.Factory.StartNew(async () =>
{
double i = 0;
while (i < 3)
{
i++;
await PauseToken.WaitWhilePausedAsync();
Thread.Sleep(5000);
}
}
);
await task;
}
catch (Exception e)
{
isError = true;
message = e.Message;
}
if (!isError) message = task.IsCompleted ? "Completed" : "Canceled";
MessageBox.Show(message);
FlagCommandDone = false;
}
EDIT:
task = Task.Factory.StartNew(async () =>
{
double i = 0;
while (i < 3)
{
i++;
CurrentStatus = i.ToString();
var a = (int)(double)((i / 3.0) * 100.0);
MessageBox.Show(PauseToken.IsPaused.ToString());
await PauseToken.WaitWhilePausedAsync();
CancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(5000);
CurrentProgress = a;
}
}
, CancellationToken);

The problem with your code is that the task created by StartNew() is deemed complete when the invoked method returns. And of course, it being an async method with an await, that method returns as soon as the first await is executed on an incomplete task.
So, your DoDoneAsync() method is in fact completing before the whole you thought you were starting completes. It essentially is waiting only until your StartNew() method reaches its first await.
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces your problem, it's impossible to know for sure what will fix your program. But, I think the main thing is to stop trying to mix blocking code with async code. Instead, just factor the anonymous method you have invoked by StartNew() out into its own named method, make it properly async, and just call it directly.
For example:
private async Task DoDoneAsync()
{
FlagCommandDone = true;
try
{
await DoDoneLoopAsync();
}
catch (Exception e)
{
isError = true;
message = e.Message;
}
if (!isError) message = task.IsCompleted ? "Completed" : "Canceled";
MessageBox.Show(message);
FlagCommandDone = false;
}
private async Task DoDoneLoopAsync()
{
double i = 0;
while (i < 3)
{
i++;
await PauseToken.WaitWhilePausedAsync();
await Task.Delay(5000);
}
}

I needed to use Task.Run() instead Task.Factory.StartNew().
Task.Run vs Task.Factory.StartNew is here.
Thanks to VladD.

Related

moving from synchronous API to async API

I need to move from using a synchronous API to async API:
void Client()
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
int numAttempts = SendWithRetries();
stopWatch.Stop();
Logging.Log(LoggingLevel.Info, string.Format("time taken {0} ", numEvents, partitionId, stopWatch.Elapsed.TotalSeconds.ToString()));
}
private int SendWithRetries(int numRetries = 3)
{
for (int i = 0; i <= numRetries; i++)
{
try
{
API();
return i;
}
catch (Exception e)
{
if (i == numRetries)
{
throw;
}
}
}
return -1;
}
Now to move to async API, I gather from internet that I need to replace API with
await APIAsync()
I am a little confused on this. When I add await, it would force the main thread to wait for completion of APIAsync How is that different from the synchronous call?
What if I do the following change and continue calling API() within SendWithRetries method:
void Client()
{
Task newTask =
System.Threading.Tasks.Task.Run(() => {
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
int numAttempts = SendWithRetries();
stopWatch.Stop();
Logging.Log(LoggingLevel.Info, string.Format("### time taken {0} ", numEvents, partitionId, stopWatch.Elapsed.TotalSeconds.ToString()));
});
newTask.Wait();
}
Why is using the async method with await better than the above approach?
Also, whats wrong with the following:
private int SendWithRetries(int numRetries = 3)
{
for (int i = 0; i <= numRetries; i++)
{
try
{
APIAsync();
return i;
}
catch (Exception e)
{
if (i == numRetries)
{
throw;
}
}
}
return -1;
}
How is that different from the synchronous call?
Asynchronous code doesn't block the calling thread.
Why is using the async method with await better than the above approach?
That approach moves the blocking to a thread pool thread (Task.Run). Asynchronous code doesn't block a thread pool thread.
Also, whats wrong with the following
Since the code ignores the returned Task, the code can never know when the API call completes, or whether it had an error.
More information:
Async and await
Async best practices
The await keyword allows the application to wait without becoming non-responsive. The user will be able to continue interacting with the application, while the awaited operation is running in the background. When the operation completes, the code after the await will execute, by default in the UI thread.
You may need to restrict the user from certain interactions for the duration of the awaited operation. Some buttons or menu options may better become disabled, so that the user cannot initiate multiple asynchronous operations, and cause resources starvation or other problems.

C# How to await async task until it indicates to proceed

I have a C# method, which calls an external web service multiple times, in a loop. I need to call it asynchronously in a different thread.
But the caller process MUST wait until the async thread meets a certain condition, - this condition occurs much before the loop iterations complete.
Please suggest a C# code example which describes how to wait until the async block of code indicates that a certain condition has been met, so that the main process can proceed without waiting for loop to finish.
My code:
..
List<MyObject> objList = GetObjects();
int counter = 0;
await Task.Factory.StartNew(() =>
{
foreach (MyObject obj in objList)
{
counter++;
CallExtWebSvc(obj);
if (counter == 1)
{
// return an indication that main process can proceed.
}
}
});
// Do other stuff...
You could execute your method as fire and forget and then wait for a TaskCompletionSource. This TaskCompletionSource is given to the method that calls the webservice as parameter. The method then sets a result on the TaskCompletionSource at some point.
Here is an example code piece:
public async Task DoWebserviceStuffAsync(TaskCompletionSource<bool> taskCompletionSource)
{
for (int i = 0; i < 10; i++)
{
//your webservice call
await Task.Delay(5000);
//some condition
if (i == 1)
{
//after setting this your CallingMethod finishes
//waiting the await taskCompletionSource.Task;
taskCompletionSource.TrySetResult(true);
}
}
}
private async Task CallerMethod()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
//call method without await
//care: You cannot catch exceptions without await
DoWebserviceStuffAsync(taskCompletionSource);
//wait for the DoWebserviceStuffAsync to set a result on the passed TaskCompletionSource
await taskCompletionSource.Task;
}
If you want to avoid the danger of "fire and forget" or you also need to wait for the full operation to complete, you could return two tasks (Task,Task) (C# v7 syntax). The caller would await both tasks in order.
public async Task Caller()
{
var (partOne,partTwo) = DoSomethingAsync();
await partOne;
//part one is done...
await partTwo;
//part two is done...
}
public (Task,Task) DoSomethingAsync()
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
return (tcs.Task, DoWork(tcs));
}
public async Task DoWork(TaskCompletionSource<bool> tcs)
{
List<MyObject> objList = GetObjects();
int counter = 0;
await Task.Run(() =>
{
foreach (MyObject obj in objList)
{
counter++;
CallExtWebSvc(obj);
if (counter == 1)
{
// return an indication that main process can proceed.
tcs.SetResult(true);
}
}
});
// Do other stuff...
}

Correct Way to do Task Synchronization?

Is what I'm doing below the correct/best way to accomplish this?
I have a window with a timer. Each time the timer ticks, I call the RunTask method shown below. Within RunTask, I call DoTheThing. DoTheThing may take a while to run, and may fail (it's a database update). I want to make sure that at any point in time, I only have one DoTheThing outstanding. I also want to make sure that I don't have a bunch of RunTask instances all queued and waiting for a lock to be released by the RunTask instance that is running DoTheThing.
public void RunTask()
{
bool canRunTask = true;
// Check if another instance of this method is currently executing. If so, do not execute the rest of this method
lock (this.runTaskLock)
{
if (this.isTaskRunning)
{
canRunTask = false;
}
else
{
this.isTaskRunning = true;
}
}
// Call DoTheThing if another instance is not currently outstanding
if (canRunTask)
{
try
{
Task task = new Task(() => DoTheThing());
task.Start();
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
lock (this.runTaskLock)
{
this.isTaskRunning = false;
}
}
}
}
Because of the architecture of the program, I would rather put all of my thread synchronization within this method instead of enabling and disabling the timer.
By thinking about the problem slightly differently, it becomes a lot easier. Instead of firing a timer every x seconds, why not wait x seconds between invocations?
Now you can just run an async loop to do the scheduled work and save yourself a bunch of painful synchronization work.
async Task RunActionPeriodicallyAsync(Action action,
TimeSpan ts,
CancellationToken token = default(CancellationToken))
{
while(!token.IsCancellationRequested)
{
action();
await Task.Delay(ts, token);
//or alternatively (see comment below)
//var delayTask = Task.Delay(ts, token);
//action();
//await delayTask;
}
}
Now, just call RunActionPeriodicallyAsync once, and calls to its action will never overlap.
RunActionPeriodicallyAsync(() => DoSomething(), TimeSpan.FromSeconds(10))
You could overload this to take an async "action"... actually a Func<Task>...
async Task RunActionPeriodicallyAsync(Func<CancellationToken, Task> actionAsync,
TimeSpan ts,
CancellationToken token = default(CancellationToken))
{
while(!token.IsCancellationRequested)
{
await actionAsync(token);
await Task.Delay(ts, token);
//or alternatively (see comment below)
//await Task.WhenAll(actionAsync(token), Task.Delay(ts, token))
}
}
and use it:
RunActionPeriodicallyAsync(async cancTok => await DoSomethingAsync(cancTok),
TimeSpan.FromSeconds(10))
If you are worried about too much locking, you can do the following. You might miss a run if one task completes while the other is just at the check (marked), but you got rid of some locking and you will only need to lock when you set isTaskRunnung = true.
In Addition you need to mark your method as async so you can await the task.
public async Task RunTask()
{
bool canRunTask = true;
// Check if another instance of this method is currently executing. If so, do not execute the rest of this method
if (this.isTaskRunning)
{ // <-- ___MARK___
canRunTask = false;
}
else
{
lock (this.runTaskLock)
{
if (this.isTaskRunning)
{
canRunTask = false;
}
else
{
this.isTaskRunning = true;
}
}
}
// Call DoTheThing if another instance is not currently outstanding
if (canRunTask)
{
try
{
await Task.Run(() => DoTheThing());
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
this.isTaskRunning = false;
}
}
}

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.

Why does task wait for Task.Run before executing next code but not for Task.Factory.StartNew?

At least as I've implemented it in my code, I had to modify the StartNew Task to get the same behavior. In my View there's a start button. It's IsEnabled property is bound to a Boolean in the View Model. Without adding await task.ContinueWith(_ => true); and moving return true; out of the try block, the PopulateListStartNew Task doesn't wait so the button stays enabled. I prefer to use Task.Factory.StartNew because passing a TaskScheduler makes for more readable code (no Dispatcher clutter). Records is an ObservableCollection.
I thought that Task.Run was basically a shortcut (per Task.Run vs Task.Factory.StartNew. At any rate, I'd like to better understand the difference in behavior and would certainly appreciate any suggestions related to making my example code better.
public async Task<bool> PopulateListTaskRun(CancellationToken cancellationToken)
{
try
{
await Task.Run(async () =>
{
// Clear the records out first, if any
Application.Current.Dispatcher.InvokeAsync(() => Records.Clear());
for (var i = 0; i < 10; i++)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
// Resharper says do this to avoid "Access to modified closure"
var i1 = i;
Application.Current.Dispatcher.InvokeAsync(() =>
{
Records.Add(new Model
{
Name = NamesList[i1],
Number = i1
});
Status = "cur: " +
i1.ToString(
CultureInfo.InvariantCulture);
});
// Artificial delay so we can see what's going on
await Task.Delay(200);
}
Records[0].Name = "Yes!";
}, cancellationToken);
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<bool> PopulateListStartNew(CancellationToken cancellationToken, TaskScheduler taskScheduler)
{
try
{
var task = await Task.Factory.StartNew(async () =>
{
// Clear the records out first, if any
Records.Clear();
for (var i = 0; i < 10; i++)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
Records.Add(new Model
{
Name = NamesList[i],
Number = i
});
Status = "cur: " +
i.ToString(
CultureInfo.InvariantCulture);
// Artificial delay so we can see what's going on
await Task.Delay(200);
}
Records[0].Name = "Yes!";
}, cancellationToken, TaskCreationOptions.None, taskScheduler);
// Had to add this
await task.ContinueWith(_ => true);
}
catch (Exception)
{
return false;
}
// Had to move this out of try block
return true;
}
The link you posted in your question has the answer: Task.Run understands and unwraps async Task delegates, while StartNew returns a Task<Task> instead, which you have to unwrap yourself by calling Unwrap or doing a double-await.
However, I recommend you completely rewrite the code as follows. Notes:
Don't use Dispatcher. There shouldn't be a need for it with properly-written async code.
Treat all your background worker methods and asynchronous operations as "services" for your UI thread. So your method will return to the UI context periodically as necessary.
Like this:
public async Task<bool> PopulateListTaskRunAsync(CancellationToken cancellationToken)
{
try
{
// Clear the records out first, if any
Records.Clear();
for (var i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
Records.Add(new Model
{
Name = NamesList[i],
Number = i
});
Status = "cur: " + i.ToString(CultureInfo.InvariantCulture);
// Artificial delay so we can see what's going on
await Task.Delay(200);
}
Records[0].Name = "Yes!";
return true;
}
catch (Exception)
{
return false;
}
}
I'm too not comfortable with all this plumbing but I'll try to answer.
First why your second code does not work:
you give StartNew an async delegate which is something like Func<Task> so StartNew will return a Task<Task> and you wait on the outer task which ends immediately as it consists in returning the inner Task (not really sure about that)
then you await the continuation of the inner task, the inner thread of execution, what you intended to do; but I guess it should be the same if you awaited directly the inner task itself this way:
await await Task.Factory.StartNew(async ...
Why your first code works:
according to the MSDN documentation Task.Run directly returns a Task object, the inner task I guess
so you directly await for the inner task, not an intermediate one, so it just works as expected
At least this is my understanding and keep in mind I've not yet played with all this stuff (no VS 2012). :)

Categories

Resources