CancellationTokenSource does not cancel the task - c#

I have Two button for scenarios where one initiate the task and other stop that task.
// this is the property which is used to cancel the task
CancellationTokenSource cTokenSource;
private async void OnReadCommand()
{
cTokenSource = new CancellationTokenSource();
ReadAction();
}
private async void ReadAction()
{
Task.Factory.StartNew(() => {
while (true)
{
Task.Delay(TimeSpan.FromSeconds(2)).Wait();
//writing in debug console
Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
//sending it to the ui for testing
uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
}
},cTokenSource.Token);
}
private async void OnCancelCommand()
{
// it's used to cancel the task
cTokenSource.Cancel();
}
my app is wpf and using mvvm pattern and prism library.
while calling the OnCancelCommand the task is running in background and printing the GUID.
I have to cancel the task only and only on OnCancelCommand.

You need to implement the code for exiting the loop yourself:
private async void ReadAction()
{
Task.Factory.StartNew(() => {
while (true)
{
Task.Delay(TimeSpan.FromSeconds(2)).Wait();
//writing in debug console
Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
//sending it to the ui for testing
uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
cTokenSource.Token.ThrowIfCancellationRequested();
}
},cTokenSource.Token);
}
or
private async void ReadAction()
{
Task.Factory.StartNew(() => {
while (true)
{
Task.Delay(TimeSpan.FromSeconds(2)).Wait();
//writing in debug console
Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
//sending it to the ui for testing
uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
if (cTokenSource.Token.IsCancellationRequested)
{
break; // or return;
}
}
},cTokenSource.Token);
Usage examples you can find in the documenation Task Class. The only benefit that you get from passing CancellationToken to StartNew method is that it can automatically cancel the task for you if the source is cancelled before the task started. However to cancel it during the run you need to code the logic yourself. Here is another good reading on that https://stackoverflow.com/questions/48312544/whats-the-benefit-of-passing-a-cancellationtoken-as-a-parameter-to-task-run#:~:text=In%20summary%2C%20providing%20the%20cancellation,to%20start%20running%20the%20task.
Furthermore, I would suggest to you using await in the logic that you execute in StartNew so that you do not loose the benefits of being asynchronous:
private async void ReadAction()
{
Task.Factory.StartNew(async () => {
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(2));
//writing in debug console
Debug.WriteLine($"Some Random Nos : {Guid.NewGuid().ToString()}");
//sending it to the ui for testing
uiContext.Send(x => MessageArea2Content.Message = Guid.NewGuid().ToString(), null);
if (cTokenSource.Token.IsCancellationRequested)
{
break; // or return;
}
}
},cTokenSource.Token)
That way threads will no longer get blocked and will be released during a call to Delay.

Related

Cancel Task running under User Control

In my project I have few User controls changed by navigation. One of controls runs Tasks. I do it like this:
public partial class uc_WorkingArea : UserControl
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token;
public uc_WorkingArea()
{
InitializeComponent();
this.Unloaded += uc_WorkingArea_Unloaded;
token = cts.Token;
Task Printer1 = Task.Run(() => PrinterPooler(lst_PrinterStruct, 0), cts.Token);
}
private void uc_WorkingArea_Unloaded(object sender, RoutedEventArgs e)
{
cts.Cancel();
if (token.CanBeCanceled)
{
MessageBox.Show("Can be canceled");
}
if (token.IsCancellationRequested)
{
MessageBox.Show("Canceled requested");
}
cts.Cancel();
MessageBox.Show("Status: " + Printer1.Status.ToString());
}
}
When I leave current user control and switching to another uc_WorkingArea_Unloaded executes. I see messages, that Task can be canceled and request to cancel accepted.
But, current status of Printer1 task still "IsRunning".
So, If I return back to this user control, Task starts again and Application had two running similar tasks.
I tried run task under Factory, like this
Task Printer1 = Task.Factory.StartNew(() => PrinterPooler(lst_PrinterStruct, 0), cts.Token);
But without success. App still runs two similar tasks.
PrinterPooler method not async.
I can't understand where mistake was made. Your help guys needed.
You have to pass the token into the PrintPooler method, and there inside check if it should be cancelled.
for(int i = 0; i < 10000; i++)
{
DoStuff();
cancelToken.ThrowIfCancellationRequested(); // if tasks end with this exception, it knows the work has been cancelled
}
Canceling a Task does not stop the execution, it only gives signal to code inside that it should end and sets the task status to Cancelled/Faulted/RanToCompletion depending on how execution stops.
Note that you need to pass the same token to the Task and to the method that will throw it.
Regarding to this post How do I abort/cancel TPL Tasks?
You have to Implement your cancle condition by your self. For example:
public partial class uc_WorkingArea : UserControl
{
public CancellationTokenSource cts = new CancellationTokenSource();
public CancellationToken token;
public Task Printer1;
public uc_WorkingArea()
{
token = cts.Token;
Printer1 = Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
{
Console.WriteLine("run");
Application.DoEvents();
}
}, token);
}
}
Cancel Call:
uc_WorkingArea gc = new uc_WorkingArea();
for (int i = 0; i < 10; i++) //PASS SOME TIME
{
Application.DoEvents(); //CONSOLE SHOULD SPAM 'RUN' FROM TASK
Thread.Sleep(1);
}
gc.cts.Cancel(); //CANCEL CALL, WHILE LOOP END
if (gc.token.IsCancellationRequested)
{
Console.WriteLine("stop");
MessageBox.Show("Canceled requested");
}
gc.cts.Dispose();
gc.Printer1.Dispose();
Hope it helps.

C# skips DispatcherOperation

I have a big time-consuming task and I try to implement asynchronous methods in order to prevent the application from blocking. My code looks like this:
CancellationTokenSource _cts;
async void asyncMethod()
{
// ..................
_cts = new CancellationTokenSource();
var progress = new Progress<double>(value => pbCalculationProgress.Value = value);
try
{
_cts.CancelAfter(25000);
int count = await awaitMethod(_cts.Token, progress);
}
catch (OperationCanceledException ex)
{
// .......
}
finally
{
_cts.Dispose();
}
// ..................
}
async Task<int> awaitMethod(CancellationToken ct, IProgress<double> progress)
{
var task = Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
sqlParser();
progress.Report(1);
return 0;
});
return await task;
}
void sqlParser()
{
string info = form1TxtBox.Text;
// ................
}
Also, the program throws an exception, because sqlParser() updates UI thread, when it retrieves the text from the form. The solution is to introduce Dispatcher method, which allows UI update. I keep the body of awaitMethod the same and simply put sqlParser() inside of the Dispatcher:
DispatcherOperation op = Dispatcher.BeginInvoke((Action)(() =>
{
sqlParser();
}));
Here happens something interesting: asyncMethod() even doesn't dare to call awaitMethod! However, if I put a breakpoint inside of sqlParser() and run debugger, then everything goes very smoothly.
Please, can somebody explain what I miss in my code? What kind of patch should i use to make Dispatcher work correctly? Or: how can I run my program without Dispatcher and without throwing UI-update exception?
The solution is to introduce Dispatcher method, which allows UI update.
That's never a good solution. Having background threads reaching directly into your UI is encouraging spaghetti code.
how can I run my program without Dispatcher and without throwing UI-update exception?
Think of your background thread code as its own separate component, completely separate from the UI. If your background code needs data from the UI, then have your UI code read it before the background code starts, and pass that data into the background code.
async void asyncMethod()
{
...
try
{
var data = myUiComponent.Text;
_cts.CancelAfter(25000);
int count = await awaitMethod(data, _cts.Token, progress);
}
...
}
async Task<int> awaitMethod(string data, CancellationToken ct, IProgress<double> progress)
{
var task = Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
sqlParser(data);
progress.Report(1);
return 0;
});
return await task;
}

How to handle task cancellation in the TPL

Good day! I am writing a helper library for WinForms UI. Started using TPL async/await mechanism and got a problem with this kind of code example :
private SynchronizationContext _context;
public void UpdateUI(Action action)
{
_context.Post(delegate { action(); }, null);
}
private async void button2_Click(object sender, EventArgs e)
{
var taskAwait = 4000;
var progressRefresh = 200;
var cancellationSource = new System.Threading.CancellationTokenSource();
await Task.Run(() => { UpdateUI(() => { button2.Text = "Processing..."; }); });
Action usefulWork = () =>
{
try
{
Thread.Sleep(taskAwait);
cancellationSource.Cancel();
}
catch { }
};
Action progressUpdate = () =>
{
int i = 0;
while (i < 10)
{
UpdateUI(() => { button2.Text = "Processing " + i.ToString(); });
Thread.Sleep(progressRefresh);
i++;
}
cancellationSource.Cancel();
};
var usefulWorkTask = new Task(usefulWork, cancellationSource.Token);
var progressUpdateTask = new Task(progressUpdate, cancellationSource.Token);
try
{
cancellationSource.Token.ThrowIfCancellationRequested();
Task tWork = Task.Factory.StartNew(usefulWork, cancellationSource.Token);
Task tProgress = Task.Factory.StartNew(progressUpdate, cancellationSource.Token);
await Task.Run(() =>
{
try
{
var res = Task.WaitAny(new[] { tWork, tProgress }, cancellationSource.Token);
}
catch { }
}).ConfigureAwait(false);
}
catch (Exception ex)
{
}
await Task.Run(() => { UpdateUI(() => { button2.Text = "button2"; }); });
}
Basically, the idea is to run two parallel tasks - one is for, say, progress bar or whatever update and a sort of timeout controller, the other is the long running task itself. Whichever task finishes first cancels the other one. So, there should not be a problem to cancel the "progress" task as it has a loop in which I can check if task is marked cancelled. The problem is with the long running one. It could be Thread.Sleep() or SqlConnection.Open(). When I run CancellationSource.Cancel(), the long running task keeps working and does not cancel. After a timeout I am not interested in long running task or whatever it may result in.
As the cluttered code example may suggest, I have tried a bunch of variants and none given me a desired effect. Something like Task.WaitAny() freezes UI... Is there a way to make that cancellation work or may be even a different approach to code these things?
UPD:
public static class Taskhelpers
{
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
}
return await task;
}
public static async Task WithCancellation(this Task task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
}
await task;
}
}
.....
var taskAwait = 4000;
var progressRefresh = 200;
var cancellationSource = new System.Threading.CancellationTokenSource();
var cancellationToken = cancellationSource.Token;
var usefulWorkTask = Task.Run(async () =>
{
try
{
System.Diagnostics.Trace.WriteLine("WORK : started");
await Task.Delay(taskAwait).WithCancellation(cancellationToken);
System.Diagnostics.Trace.WriteLine("WORK : finished");
}
catch (OperationCanceledException) { } // just drop out if got cancelled
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine("WORK : unexpected error : " + ex.Message);
}
}, cancellationToken);
var progressUpdatetask = Task.Run(async () =>
{
for (var i = 0; i < 25; i++)
{
if (!cancellationToken.IsCancellationRequested)
{
System.Diagnostics.Trace.WriteLine("==== : " + i.ToString());
await Task.Delay(progressRefresh);
}
}
},cancellationToken);
await Task.WhenAny(usefulWorkTask, progressUpdatetask);
cancellationSource.Cancel();
By modifying for (var i = 0; i < 25; i++) limit of i I imitate whether long running task finishes before the progress task or otherwise. Works as desired. The WithCancellation helper method does the job, although two sort of 'nested' Task.WhenAny look suspicious for now.
I agree with all the points in Paulo's answer - namely, use modern solutions (Task.Run instead of Task.Factory.StartNew, Progress<T> for progress updates instead of manually posting to the SynchronizationContext, Task.WhenAny instead of Task.WaitAny for asynchronous code).
But to answer the actual question:
When I run CancellationSource.Cancel(), the long running task keeps working and does not cancel. After a timeout I am not interested in long running task or whatever it may result in.
There are two parts to this:
How do I write code that responds to a cancellation request?
How do I write code that ignores any responses after the cancellation?
Note that the first part deals with cancelling the operation, and the second part is actually dealing with cancelling the waiting for the operation to complete.
First things first: support cancellation in the operation itself. For CPU-bound code (i.e., running a loop), periodically call token.ThrowIfCancellationRequested(). For I/O-bound code, the best option is to pass the token down to the next API layer - most (but not all) I/O APIs can (should) take cancellation tokens. If this isn't an option, then you can either choose to ignore the cancellation, or you can register a cancellation callback with token.Register. Sometimes there's a separate cancellation method you can call from your Register callback, and sometimes you can make it work by disposing the object from the callback (this approach often works because of a long-standing Win32 API tradition of cancelling all I/O for a handle when that handle is closed). I'm not sure if this will work for SqlConnection.Open, though.
Next, cancelling the wait. This one is relatively simple if you just want to cancel the wait due to a timeout:
await Task.WhenAny(tWork, tProgress, Task.Delay(5000));
When you write something like await Task.Run(() => { UpdateUI(() => { button2.Text = "Processing..."; }); }); on your button2_Click, you are, from the UI thread, scheduling an action to a thread poll thread that posts an action to the UI thread. If you called the action directly, it would be quickier because it wouldn't have two context switchings.
ConfigureAwait(false) causes the synchronization context to not being captured. I should not be used inside UI methods because, you most certainely, want to do some UI work on the continuation.
You shouldn't use Task.Factory.StartNew instead of Task.Run unless you absolutely have a reason to. See this and this.
For progress updates, consider using the Progress<T> class, because it captures the synchronization context.
Maybe you should try something like this:
private async void button2_Click(object sender, EventArgs e)
{
var taskAwait = 4000;
var cancellationSource = new CancellationTokenSource();
var cancellationToken = cancellationSource.Token;
button2.Text = "Processing...";
var usefullWorkTask = Task.Run(async () =>
{
try
{
await Task.Dealy(taskAwait);
}
catch { }
},
cancellationToken);
var progress = new Progress<imt>(i => {
button2.Text = "Processing " + i.ToString();
});
var progressUpdateTask = Task.Run(async () =>
{
for(var i = 0; i < 10; i++)
{
progress.Report(i);
}
},
cancellationToken);
await Task.WhenAny(usefullWorkTask, progressUpdateTask);
cancellationSource.Cancel();
}
I think you need to check IsCancellationRequested in the progressUpdate Action.
As to how to do what you want, this blog discusses an Extension method WithCancellation that will make it so that you stop waiting for your long running task.

TaskContinuationOptions.RunContinuationsAsynchronously and Stack Dives

In this blog post, Stephan Toub describes a new feature that will be included in .NET 4.6 which adds another value to the TaskCreationOptions and TaskContinuationOptions enums called RunContinuationsAsynchronously.
He explains:
"I talked about a ramification of calling {Try}Set* methods on
TaskCompletionSource, that any synchronous continuations off
of the TaskCompletionSource’s Task could run synchronously as
part of the call. If we were to invoke SetResult here while holding
the lock, then synchronous continuations off of that Task would be run
while holding the lock, and that could lead to very real problems.
So, while holding the lock we grab the TaskCompletionSource to
be completed, but we don’t complete it yet, delaying doing so until
the lock has been released"
And gives the following example to demonstrate:
private SemaphoreSlim _gate = new SemaphoreSlim(1, 1);
private async Task WorkAsync()
{
await _gate.WaitAsync().ConfigureAwait(false);
try
{
// work here
}
finally { _gate.Release(); }
}
Now imagine that you have lots of calls to WorkAsync:
await Task.WhenAll(from i in Enumerable.Range(0, 10000) select WorkAsync());
We've just created 10,000 calls to WorkAsync that will be
appropriately serialized on the semaphore. One of the tasks will
enter the critical region, and the others will queue up on the
WaitAsync call, inside SemaphoreSlim effectively enqueueing the task
to be completed when someone calls Release. If Release completed that
Task synchronously, then when the first task calls Release, it'll
synchronously start executing the second task, and when it calls
Release, it'll synchronously start executing the third task, and so
on. If the "//work here" section of code above didn't include any
awaits that yielded, then we're potentially going to stack dive here
and eventually potentially blow out the stack.
I'm having a hard time grasping the part where he talks about executing the continuation synchronously.
Question
How could this possibly cause a stack dive? More so, And what is RunContinuationsAsynchronously effectively going to do in order to solve that problem?
The key concept here is that a task's continuation may run synchronously on the same thread that completed the antecedent task.
Let's imagine that this is SemaphoreSlim.Release's implementation (it's actually Toub's AsyncSemphore's):
public void Release()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
toRelease.SetResult(true);
}
We can see that it synchronously completes a task (using TaskCompletionSource).
In this case, if WorkAsync has no other asynchronous points (i.e. no awaits at all, or all awaits are on an already completed task) and calling _gate.Release() may complete a pending call to _gate.WaitAsync() synchronously on the same thread you may reach a state in which a single thread sequentially releases the semaphore, completes the next pending call, executes // work here and then releases the semaphore again etc. etc.
This means that the same thread goes deeper and deeper in the stack, hence stack dive.
RunContinuationsAsynchronously makes sure the continuation doesn't run synchronously and so the thread that releases the semaphore moves on and the continuation is scheduled for another thread (which one depends on the other continuation parameters e.g. TaskScheduler)
This logically resembles posting the completion to the ThreadPool:
public void Release()
{
TaskCompletionSource<bool> toRelease = null;
lock (m_waiters)
{
if (m_waiters.Count > 0)
toRelease = m_waiters.Dequeue();
else
++m_currentCount;
}
if (toRelease != null)
Task.Run(() => toRelease.SetResult(true));
}
How could this possibly cause a stack dive? More so, And what is
RunContinuationsAsynchronously effectively going to do in order to
solve that problem?
i3arnon provides a very good explanation of the reasons behind introducing RunContinuationsAsynchronously. My answer is rather orthogonal to his; in fact, I'm writing this for my own reference as well (I myself ain't gonna remember any subtleties of this in half a year from now :)
First of all, let's see how TaskCompletionSource's RunContinuationsAsynchronously option is different from Task.Run(() => tcs.SetResult(result)) or the likes. Let's try a simple console application:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplications
{
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(100, 100);
Console.WriteLine("start, " + new { System.Environment.CurrentManagedThreadId });
var tcs = new TaskCompletionSource<bool>();
// test ContinueWith-style continuations (TaskContinuationOptions.ExecuteSynchronously)
ContinueWith(1, tcs.Task);
ContinueWith(2, tcs.Task);
ContinueWith(3, tcs.Task);
// test await-style continuations
ContinueAsync(4, tcs.Task);
ContinueAsync(5, tcs.Task);
ContinueAsync(6, tcs.Task);
Task.Run(() =>
{
Console.WriteLine("before SetResult, " + new { System.Environment.CurrentManagedThreadId });
tcs.TrySetResult(true);
Thread.Sleep(10000);
});
Console.ReadLine();
}
// log
static void Continuation(int id)
{
Console.WriteLine(new { continuation = id, System.Environment.CurrentManagedThreadId });
Thread.Sleep(1000);
}
// await-style continuation
static async Task ContinueAsync(int id, Task task)
{
await task.ConfigureAwait(false);
Continuation(id);
}
// ContinueWith-style continuation
static Task ContinueWith(int id, Task task)
{
return task.ContinueWith(
t => Continuation(id),
CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
}
}
Note how all continuations run synchronously on the same thread where TrySetResult has been called:
start, { CurrentManagedThreadId = 1 }
before SetResult, { CurrentManagedThreadId = 3 }
{ continuation = 1, CurrentManagedThreadId = 3 }
{ continuation = 2, CurrentManagedThreadId = 3 }
{ continuation = 3, CurrentManagedThreadId = 3 }
{ continuation = 4, CurrentManagedThreadId = 3 }
{ continuation = 5, CurrentManagedThreadId = 3 }
{ continuation = 6, CurrentManagedThreadId = 3 }
Now what if we don't want this to happen, and we want each continuation to run asynchronously (i.e., in parallel with other continuations and possibly on another thread, in the absence of any synchronization context)?
There's a trick that could do it for await-style continuations, by installing a fake temporary synchronization context (more details here):
public static class TaskExt
{
class SimpleSynchronizationContext : SynchronizationContext
{
internal static readonly SimpleSynchronizationContext Instance = new SimpleSynchronizationContext();
};
public static void TrySetResult<TResult>(this TaskCompletionSource<TResult> #this, TResult result, bool asyncAwaitContinuations)
{
if (!asyncAwaitContinuations)
{
#this.TrySetResult(result);
return;
}
var sc = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(SimpleSynchronizationContext.Instance);
try
{
#this.TrySetResult(result);
}
finally
{
SynchronizationContext.SetSynchronizationContext(sc);
}
}
}
Now, using tcs.TrySetResult(true, asyncAwaitContinuations: true) in our test code:
start, { CurrentManagedThreadId = 1 }
before SetResult, { CurrentManagedThreadId = 3 }
{ continuation = 1, CurrentManagedThreadId = 3 }
{ continuation = 2, CurrentManagedThreadId = 3 }
{ continuation = 3, CurrentManagedThreadId = 3 }
{ continuation = 4, CurrentManagedThreadId = 4 }
{ continuation = 5, CurrentManagedThreadId = 5 }
{ continuation = 6, CurrentManagedThreadId = 6 }
Note how await continuations now run in parallel (albeit, still after all synchronous ContinueWith continuations).
This asyncAwaitContinuations: true logic is a hack and it works for await continuations only. The new RunContinuationsAsynchronously makes it work consistently for any kind of continuations, attached to TaskCompletionSource.Task.
Another nice aspect of RunContinuationsAsynchronously is that any await-style continuations scheduled to be resumed on specific synchronization context will run on that context asynchronously (using SynchronizationContext.Post, even if TCS.Task completes on the same context (unlike the current behavior of TCS.SetResult). ContinueWith-style continuations will be also be run asynchronously by their corresponding task schedulers (most often, TaskScheduler.Default or TaskScheduler.FromCurrentSynchronizationContext). They won't be inlined via TaskScheduler.TryExecuteTaskInline. I believe Stephen Toub has clarified that in the comments to his blog post, and it also can be seen here in CoreCLR's Task.cs.
Why should we be worrying about imposing asynchrony on all continuations?
I usually need it when I deal with async methods which execute cooperatively (co-routines).
A simple example is a pause-able asynchronous processing: one async process pauses/resumes the execution of another. Their execution workflow synchronizes at certain await points, and TaskCompletionSource is used for such kind of synchronization, directly or indirectly.
Below is some ready-to-play-with sample code which uses an adaptation of Stephen Toub's PauseTokenSource. Here, one async method StartAndControlWorkAsync starts and periodically pauses/resumes another async method, DoWorkAsync. Try changing asyncAwaitContinuations: true to asyncAwaitContinuations: false and see the logic being completely broken:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
static void Main()
{
StartAndControlWorkAsync(CancellationToken.None).Wait();
}
// Do some work which can be paused/resumed
public static async Task DoWorkAsync(PauseToken pause, CancellationToken token)
{
try
{
var step = 0;
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Working, step: " + step++);
await Task.Delay(1000).ConfigureAwait(false);
Console.WriteLine("Before await pause.WaitForResumeAsync()");
await pause.WaitForResumeAsync();
Console.WriteLine("After await pause.WaitForResumeAsync()");
}
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
throw;
}
}
// Start DoWorkAsync and pause/resume it
static async Task StartAndControlWorkAsync(CancellationToken token)
{
var pts = new PauseTokenSource();
var task = DoWorkAsync(pts.Token, token);
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Press enter to pause...");
Console.ReadLine();
Console.WriteLine("Before pause requested");
await pts.PauseAsync();
Console.WriteLine("After pause requested, paused: " + pts.IsPaused);
Console.WriteLine("Press enter to resume...");
Console.ReadLine();
Console.WriteLine("Before resume");
pts.Resume();
Console.WriteLine("After resume");
}
}
// Based on Stephen Toub's PauseTokenSource
// http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
// the main difference is to make sure that when the consumer-side code - which requested the pause - continues,
// the producer-side code has already reached the paused (awaiting) state.
// E.g. a media player "Pause" button is clicked, gets disabled, playback stops,
// and only then "Resume" button gets enabled
public class PauseTokenSource
{
internal static readonly Task s_completedTask = Task.Delay(0);
readonly object _lock = new Object();
bool _paused = false;
TaskCompletionSource<bool> _pauseResponseTcs;
TaskCompletionSource<bool> _resumeRequestTcs;
public PauseToken Token { get { return new PauseToken(this); } }
public bool IsPaused
{
get
{
lock (_lock)
return _paused;
}
}
// request a resume
public void Resume()
{
TaskCompletionSource<bool> resumeRequestTcs = null;
lock (_lock)
{
resumeRequestTcs = _resumeRequestTcs;
_resumeRequestTcs = null;
if (!_paused)
return;
_paused = false;
}
if (resumeRequestTcs != null)
resumeRequestTcs.TrySetResult(true, asyncAwaitContinuations: true);
}
// request a pause (completes when paused state confirmed)
public Task PauseAsync()
{
Task responseTask = null;
lock (_lock)
{
if (_paused)
return _pauseResponseTcs.Task;
_paused = true;
_pauseResponseTcs = new TaskCompletionSource<bool>();
responseTask = _pauseResponseTcs.Task;
_resumeRequestTcs = null;
}
return responseTask;
}
// wait for resume request
internal Task WaitForResumeAsync()
{
Task resumeTask = s_completedTask;
TaskCompletionSource<bool> pauseResponseTcs = null;
lock (_lock)
{
if (!_paused)
return s_completedTask;
_resumeRequestTcs = new TaskCompletionSource<bool>();
resumeTask = _resumeRequestTcs.Task;
pauseResponseTcs = _pauseResponseTcs;
_pauseResponseTcs = null;
}
if (pauseResponseTcs != null)
pauseResponseTcs.TrySetResult(true, asyncAwaitContinuations: true);
return resumeTask;
}
}
// consumer side
public struct PauseToken
{
readonly PauseTokenSource _source;
public PauseToken(PauseTokenSource source) { _source = source; }
public bool IsPaused { get { return _source != null && _source.IsPaused; } }
public Task WaitForResumeAsync()
{
return IsPaused ?
_source.WaitForResumeAsync() :
PauseTokenSource.s_completedTask;
}
}
}
public static class TaskExt
{
class SimpleSynchronizationContext : SynchronizationContext
{
internal static readonly SimpleSynchronizationContext Instance = new SimpleSynchronizationContext();
};
public static void TrySetResult<TResult>(this TaskCompletionSource<TResult> #this, TResult result, bool asyncAwaitContinuations)
{
if (!asyncAwaitContinuations)
{
#this.TrySetResult(result);
return;
}
var sc = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(SimpleSynchronizationContext.Instance);
try
{
#this.TrySetResult(result);
}
finally
{
SynchronizationContext.SetSynchronizationContext(sc);
}
}
}
}
I didn't want to use Task.Run(() => tcs.SetResult(result)) here, because it would be redundant to push continuations to ThreadPool when they're already scheduled to run asynchronously on a UI thread with a proper synchronization context. At the same time, if both StartAndControlWorkAsync and DoWorkAsync run on the same UI synchronization context, we'd also have a stack dive (if tcs.SetResult(result) is used without Task.Run or SynchronizationContext.Post wrapping).
Now, RunContinuationsAsynchronously is probably the best solution to this problem.

Using async socket in Tasks

I have an application that is processing items in a FIFO queue using Tasks in .net 4.0.
I am new to TPL and Tasks in .net and was wondering if there is an easy solution to my problem:
The Action delegate in the Task is assigned to a method that is sending and receiving data on an asynchronous socket. The problem I am having is that the Task ends "prematurely". How do I tell the Task to wait until all communication is completed before processing the next item in the queue?
One solution is to switch to using a synchronous socket, but I was hoping there is a way to do this using the async socket.
EDIT
Added some code:
class Program
{
private BlockingCollection<string> myQueue;
private CancellationTokenSource cancellationSignalForConsumeTask;
private CancellationTokenSource cancellationSignalForProcessCommandTask;
private AsyncSocket mySocket;
public void Main(string[] args)
{
mySocket = new mySocket();
myscoket.ReceiveData += mySocket_ReceiveData;
cancellationSignalForConsumeTask = new CancellationTokenSource();
Task listenerTask = Task.Factory.StartNew((obj) => Consume(),
cancellationSignalForConsumeTask.Token,
TaskCreationOptions.LongRunning);
while (true)
{}
}
private void Consume()
{
while (!myQueue.IsCompleted )
{
string _item = myQueue.Take();
cancellationSignalForProcessCommandTask = new CancellationTokenSource();
Task t = new Task(() =>
{
cancellationSignalForProcessCommandTask.Token.ThrowIfCancellationRequested();
DoSomeWork(_item);
}, cancellationSignalForProcessCommandTask.Token, TaskCreationOptions.LongRunning);
t.Start();
t.Wait();
}
}
private void DoSomeWork(string _item)
{
mySocket.SendData("Data to server that could take a long time to process")
}
private void mySocket_ReceiveData(string dataFromServer)
{
string returnMessage = dataFromServer;
//I want the Task to end here...
}
}
The problem is that the Task ends when the DoSomeWork() method finishes (and I understand why), is there a way I can manually tell the Task to end through the CancellationTokenSource object maybe?
If I understand correctly, you want to wait on the task the receives the data, but you're currently waiting on the task that sends the data. One way to do so would be to use a construct like AutoResetEvent:
private AutoResetEvent autoResetEvent = new AutoResetEvent(false);
private void Consume()
{
while (!myQueue.IsCompleted )
{
string _item = myQueue.Take();
cancellationSignalForProcessCommandTask = new CancellationTokenSource();
Task t = new Task(() =>
{
cancellationSignalForProcessCommandTask.Token.ThrowIfCancellationRequested();
DoSomeWork(_item);
}, cancellationSignalForProcessCommandTask.Token, TaskCreationOptions.LongRunning);
t.Start();
// Wait for data to be received.
// This line will block until autoResetEvent.Set() is called.
autoResetEvent.WaitOne();
}
}
private void mySocket_ReceiveData(string dataFromServer)
{
string returnMessage = dataFromServer;
// Notify other threads that data was received and that processing can continue.
autoResetEvent.Set();
}
This is only an example of using AutoResetEvent - you'd probably want to refine it to suit your needs.

Categories