I expected the following Connected and Disconnected handlers are called alternately.
But it wasn't. It is not 100% reproducible but sometimes the test failed. Repository is here.
I attach simplified code here because full code will be long. Please refer to the repository for the reproducible example.
public class ClientStressTest3 {
[Fact]
public async Task TestAsync() {
var client = new Client();
int openCloseDifference = 0;
var failures = Channel.CreateUnbounded<string>();
client.Connected += () => {
Interlocked.Increment(ref openCloseDifference);
int difference = openCloseDifference;
Debug.WriteLine("Connected: {}", difference);
if (Math.Abs(difference) > 1) {
// Failure point. Why enter here?
_ = failures.Writer.WriteAsync($"open close difference {difference}");
}
};
client.Disconnected += () => {
Interlocked.Decrement(ref openCloseDifference);
int difference = openCloseDifference;
Debug.WriteLine("Disconnected: {}", difference);
if (Math.Abs(difference) > 1) {
_ = failures.Writer.WriteAsync($"open close difference {difference}");
}
};
var tasks = new List<Task>();
for (int i = 0; i < 625; i++) {
tasks.Add(Task.Run(async () => {
try {
await client.ConnectAsync().ConfigureAwait(false);
}
catch (Exception ex) { }
}));
tasks.Add(Task.Run(async () => {
try {
await client.CloseAsync().ConfigureAwait(false);
}
catch (Exception ex) { }
}));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
while (await failures.Reader.WaitToReadAsync().ConfigureAwait(false)) {
string failure = await failures.Reader.ReadAsync().ConfigureAwait(false);
throw new Exception(failure);
}
}
}
class Client {
public event Action Connected = delegate { };
public event Action Disconnected = delegate { };
private readonly SemaphoreSlim _connectSemaphore = new(1);
private Task _dispatch = Task.CompletedTask;
private WebSocket _clientSocket;
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
// _dispatch may be same among multiple threads if restored at the same time.
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = _dispatch.ContinueWith((_) => DispatchEventAsync(), TaskScheduler.Default);
}
}
finally {
_connectSemaphore.Release();
}
}
private async Task DispatchEventAsync() {
try {
Connected();
}
catch (Exception exception) { }
try {
while (await events.WaitToReadAsync().ConfigureAwait(false)) {
DispatchEvent(await events.ReadAsync().ConfigureAwait(false));
}
}
catch (Exception exception) { }
try {
Disconnected();
}
catch (Exception ex) { }
}
}
I think _connectSemaphore will guard the inner code execution, and only one thread will execute DispatchEventAsync.
But it seems sometimes two threads enter DispatchEventAsync at the same time. I can't understand this.
ContinueWith is a low-level method with dangerous default behavior (link is to my blog). In this case, your code is not behaving how you think it should because ContinueWith (like StartNew) doesn't understand asynchronous delegates.
Specifically, the task returned from ContinueWith (the same task stored in _dispatch) will complete when DispatchEventAsync asynchronously yields (i.e., hits its first await that asynchronously waits). This is likely the call to WaitToReadAsync, which is after Connected and before Disconnected. So the _dispatch task completes after Connected and before Disconnected.
Ideally, you should avoid ContinueWith completely. Sometimes a local asynchronous method helps, e.g.:
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = ChainAsync(_dispatch);
}
}
finally {
_connectSemaphore.Release();
}
static async Task ChainAsync(Task dispatch)
{
await dispatch;
await DispatchEventAsync();
}
}
If you do want to continue using ContinueWith for some reason, then you can use Unwrap:
public async Task ConnectAsync() {
await _connectSemaphore.WaitAsync().ConfigureAwait(false);
try {
await _clientSocket.ConnectAsync().ConfigureAwait(false);
if (_dispatch != null) {
_dispatch = _dispatch.ContinueWith(_ => DispatchEventAsync(), TaskScheduler.Default)
.Unwrap();
}
}
finally {
_connectSemaphore.Release();
}
}
Using either of these approaches, the _dispatch task will now complete at the end of DispatchEventAsync.
I think _connectSemaphore will guard the inner code execution, and only one thread will execute DispatchEventAsync.
Nope. Because you don't wait or await _dispatch. The semaphore only protects starting the task. The execution of the task happens in the background sometime after you release the semaphore.
But I'm chaining it with ContinueWith. Isn't it sufficien
No. That just adds another task that runs after; it doesn't actually run the task or wait for it to complete. If you want to run the _dispatch and then another task while holding the semaphore, just
await _dispatch;
await DispatchEventAsync();
Related
My assertion of acceptor.IsStarted.Should().BeTrue(); (see unit test below) always fails, as it's getting evaluated too early. The call to await task returns immediately and doesn't give this.acceptor.Start() enough time to spin up.
I would like to make the startup of my FixAcceptor() more deterministic and therefor introduced the parameter TimeSpan startupDelay.
However I simply have no clue where and how I can delay the startup.
Putting an additional Thread.Sleep(startupDelay) between this.acceptor.Start() and this.IsStarted = true won't help as it will only block the worker task itself, but not the calling thread.
I hope it's clear what I'd like to archive and what I am struggling with. Thanks in advance.
public class FixAcceptor
{
// Type provided by QuickFix.net
private readonly ThreadedSocketAcceptor acceptor;
public FixAcceptor(IFixSettings settings)
{
// Shortened
}
public bool IsStarted { get; private set; }
public async void Run(CancellationToken cancellationToken, TimeSpan startupDelay)
{
var task = Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
this.acceptor.Start();
this.IsStarted = true;
while (true)
{
// Stop if token has been canceled
if (cancellationToken.IsCancellationRequested)
{
this.acceptor.Stop();
this.IsStarted = false;
cancellationToken.ThrowIfCancellationRequested();
}
// Save some CPU cycles
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}, cancellationToken);
try
{
await task;
}
catch (OperationCanceledException e)
{
Debug.WriteLine(e.Message);
}
}
}
And the corresponding consumer code
[Fact]
public void Should_Run_Acceptor_And_Stop_By_CancelationToken()
{
// Arrange
var acceptor = new FixAcceptor(new FixAcceptorSettings("test_acceptor.cfg", this.logger));
var tokenSource = new CancellationTokenSource();
// Act
tokenSource.CancelAfter(TimeSpan.FromSeconds(10));
acceptor.Run(tokenSource.Token, TimeSpan.FromSeconds(3));
// Assert
acceptor.IsStarted.Should().BeTrue();
IsListeningOnTcpPort(9823).Should().BeTrue();
// Wait for cancel event to occur
Thread.Sleep(TimeSpan.FromSeconds(15));
acceptor.IsStarted.Should().BeFalse();
}
Adding time delays to achieve determinism is not a recommended practice. You can achieve 100% determinism by using a TaskCompletionSource for controlling the completion of a task at just the right moment:
public Task<bool> Start(CancellationToken cancellationToken)
{
var startTcs = new TaskCompletionSource<bool>();
var task = Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
this.acceptor.Start();
this.IsStarted = true;
startTcs.TrySetResult(true); // Signal that the starting phase is completed
while (true)
{
// ...
}
}, cancellationToken);
HandleTaskCompletion();
return startTcs.Task;
async void HandleTaskCompletion() // async void method = should never throw
{
try
{
await task;
}
catch (OperationCanceledException ex)
{
Debug.WriteLine(ex.Message);
startTcs.TrySetResult(false); // Signal that start failed
}
catch
{
startTcs.TrySetResult(false); // Signal that start failed
}
}
}
Then replace this line in your test:
acceptor.Run(tokenSource.Token, TimeSpan.FromSeconds(3));
...with this one:
bool startResult = await acceptor.Start(tokenSource.Token);
Another issue that caught my eye is the bool IsStarted property which is mutated from one thread and observed by another, without synchronization. This is not really a problem because you could rely on the undocumented memory barrier that is inserted automatically on every await, and be quite confident that you'll not have visibility issues, but if you want to be extra sure you could synchronize the access by using a lock (most robust), or backup the property with a volatile private field like this:
private volatile bool _isStarted;
public bool IsStarted => _isStarted;
I would recommend that you structure your FixAcceptor.Run() methode a little bit different
public async Task Run(CancellationToken cancellationToken, TimeSpan startupDelay)
{
var task = Task.Run(async () =>
{
try
{
cancellationToken.ThrowIfCancellationRequested();
this.acceptor.Start();
this.IsStarted = true;
while (true)
{
// Stop if token has been canceled
if (cancellationToken.IsCancellationRequested)
{
this.acceptor.Stop();
this.IsStarted = false;
cancellationToken.ThrowIfCancellationRequested();
}
// Save some CPU cycles
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
catch (OperationCanceledException e)
{
Debut.WriteLine(e.Message);
}
}, cancellationToken);
await Task.Delay(startupDelay);
}
so the exception handling is in the inner task and the Run methode returns a Task that completes after the startupDelay.
(I also exchanged the Thread.Sleep() with a Task.Delay())
Then in the test methode you can await the Task returned by Run
[Fact]
public async Task Should_Run_Acceptor_And_Stop_By_CancelationToken()
{
// Arrange
var acceptor = new FixAcceptor(new FixAcceptorSettings("test_acceptor.cfg", this.logger));
var tokenSource = new CancellationTokenSource();
// Act
tokenSource.CancelAfter(TimeSpan.FromSeconds(10));
await acceptor.Run(tokenSource.Token, TimeSpan.FromSeconds(3));
// Assert
acceptor.IsStarted.Should().BeTrue();
IsListeningOnTcpPort(9823).Should().BeTrue();
// Wait for cancel event to occur
Thread.Sleep(TimeSpan.FromSeconds(15));
acceptor.IsStarted.Should().BeFalse();
}
It should be okay to make the mehtode async (it seams like you use xunit)
I have a method called StartAsync which await different task, this is the design:
public async Task StartAsync(int instance)
{
await SomeController.Foo();
await AnotherController.Foo2();
}
Now I need to run the StartAsync method multiple times, so I have created different Task, in this way I can manage a single execution of the Task of StartAsync:
Task bot1 = new Task(async () => { await new Bot().StartAsync(1); });
Task bot2 = new Task(async () => { await new Bot().StartAsync(2); });
these Tasks can be started by the input, essentially, if the user press 1 then the bot1 Task will start:
public async Task<bool> StartBotInstanceAsync(int instance)
{
try
{
switch(instance)
{
case 1:
await bot1.Start(); //<- Problem here
break;
case 2:
bot2.Start();
break;
}
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
Essentially I need to write a log when an exception happen in side the try catch block, but for doing this I need to await the Task result, unfortunately I get this error:
Cannot await void
on await bot1.Start();
How can I manage the exception in this type of situation?
If you wish to do it with your current structure then you will need 2 separate statements.
case 1:
bot1.Start();
await bot1;
break;
If you want to use multiple bot instances, store them in an array and use the instance parameter as the index, eg :
_bots=new[]{ new Bot(),new Bot()};
public async Task<bool> StartBotInstanceAsync(int instance)
{
try
{
await _bots[instance-1].StartAsync();
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
Or even better :
public async Task<bool> StartBotInstanceAsync(Bot bot)
{
try
{
await bot.StartAsync();
return true;
}
catch(Exception ex)
{
Logger.Info(ex.ToString());
return false;
}
}
var myBot=_bots[instance-1];
var started=await StartBotInstanceAsync(myBot);
More complex flows can be created easily by using async functions that take the bot as a parameter, eg :
public async Task SomeComplexFlow(Bot bot)
{
try
{
await bot.StartAsync();
await bot.DoSomething();
...
}
catch(Exception exc)
{
...
}
}
var myBot=_bots[instance-1];
await SomeComplexFlow(myBot);
It's possible to await multiple tasks for completion too. One coud use Task.WhenAll to wait for all bots to terminate, eg :
await Task.WhenAll(_bots[0].StopAsync()...);
Or, better yet :
var tasks=_bots.Select(bot=>bot.StopAsync());
await Tasks.WhenAll(tasks);
Supposed you have 2 async method define as bellow:
public async Task<TResult> SomeMethod1()
{
throw new Exception();
}
public async Task<TResult> SomeMethod2()
{
await Task.Delay(50);
throw new Exception();
}
Now if you await on those 2 methods the behavior will be pretty much the same. But if you are getting the task the behavior is different.
If I want to cache the result of such a computation but only when the task run to completion.
I have to take care of the 2 situation:
First Situation:
public Task<TResult> CachingThis1(Func<Task<TResult>> doSomthing1)
{
try
{
var futur = doSomthing1()
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
}
catch ()
{
// ... Remove from the pending cache
throw;
}
}
Second Situation
public Task<TResult> CachingThis2(Func<Task<TResult>> doSomthing)
{
var futur = SomeMethod2();
futur.ContinueWith(
t =>
{
// ... Add To my cache
},
TaskContinuationOptions.NotOnFaulted);
futur.ContinueWith(
t =>
{
// ... Remove from the pending cache
},
TaskContinuationOptions.OnlyOnFaulted);
}
Now I pass to my caching system the method that will execute the computation to cache.
cachingSystem.CachingThis1(SomeMethod1);
cachingSystem.CachingThis2(SomeMethod2);
Clearly I need to duplicate code in the "ConinueWith on faulted" and the catch block.
Do you know if there is a way to make the exception behave the same whether it is before or after an await?
There's no difference in the exception handling required for both SomeMethod1 and SomeMethod2. They run exactly the same way and the exception would be stored in the returned task.
This can easily be seen in this example;
static void Main(string[] args)
{
try
{
var task = SomeMethod1();
}
catch
{
// Unreachable code
}
}
public static async Task SomeMethod1()
{
throw new Exception();
}
No exception would be handled in this case since the returned task is not awaited.
There is however a distinction between a simple Task-returning method and an async method:
public static Task TaskReturning()
{
throw new Exception();
return Task.Delay(1000);
}
public static async Task Async()
{
throw new Exception();
await Task.Delay(1000);
}
You can avoid code duplication by simply having an async wrapper method that both invokes the method and awaits the returned task inside a single try-catch block:
public static async Task HandleAsync()
{
try
{
await TaskReturning();
// Add to cache.
}
catch
{
// handle exception from both the synchronous and asynchronous parts.
}
}
In addition to what I3arnon said in his answer, in case you ContinueWith on async method without the TaskContinuationOptions you specify, exception captured by the Task parameter you receive in the continuation handler can be handled in the following way:
SomeMethod1().ContinueWith(ProcessResult);
SomeMethod2().ContinueWith(ProcessResult);
With ProcessResult handler which looks like:
private void ProcessResult<TResult>(Task<TResult> task)
{
if (task.IsFaulted)
{
//remove from cahe
}
else if (task.IsCompleted)
{
//add to cache
}
}
I have a "High-Precision" timer class that I need to be able to be start, stop & pause / resume. To do this, I'm tying together a couple of different examples I found on the internet, but I'm not sure if I'm using Tasks with asnyc / await correctly.
Here is my relevant code:
//based on http://haukcode.wordpress.com/2013/01/29/high-precision-timer-in-netc/
public class HighPrecisionTimer : IDisposable
{
Task _task;
CancellationTokenSource _cancelSource;
//based on http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
PauseTokenSource _pauseSource;
Stopwatch _watch;
Stopwatch Watch { get { return _watch ?? (_watch = Stopwatch.StartNew()); } }
public bool IsPaused
{
get { return _pauseSource != null && _pauseSource.IsPaused; }
private set
{
if (value)
{
_pauseSource = new PauseTokenSource();
}
else
{
_pauseSource.IsPaused = false;
}
}
}
public bool IsRunning { get { return !IsPaused && _task != null && _task.Status == TaskStatus.Running; } }
public void Start()
{
if (IsPaused)
{
IsPaused = false;
}
else if (!IsRunning)
{
_cancelSource = new CancellationTokenSource();
_task = new Task(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning);
_task.Start();
}
}
public void Stop()
{
if (_cancelSource != null)
{
_cancelSource.Cancel();
}
}
public void Pause()
{
if (!IsPaused)
{
if (_watch != null)
{
_watch.Stop();
}
}
IsPaused = !IsPaused;
}
async void ExecuteAsync()
{
while (!_cancelSource.IsCancellationRequested)
{
if (_pauseSource != null && _pauseSource.IsPaused)
{
await _pauseSource.Token.WaitWhilePausedAsync();
}
// DO CUSTOM TIMER STUFF...
}
if (_watch != null)
{
_watch.Stop();
_watch = null;
}
_cancelSource = null;
_pauseSource = null;
}
public void Dispose()
{
if (IsRunning)
{
_cancelSource.Cancel();
}
}
}
Can anyone please take a look and provide me some pointers on whether I'm doing this correctly?
UPDATE
I have tried modifying my code per Noseratio's comments below, but I still cannot figure out the syntax. Every attempt to pass the ExecuteAsync() method to either TaskFactory.StartNew or Task.Run, results in a compilation error like the following:
"The call is ambiguous between the following methods or properties: TaskFactory.StartNew(Action, CancellationToken...) and TaskFactory.StartNew<Task>(Func<Task>, CancellationToken...)".
Finally, is there a way to specify the LongRunning TaskCreationOption without having to provide a TaskScheduler?
async **Task** ExecuteAsync()
{
while (!_cancelSource.IsCancellationRequested)
{
if (_pauseSource != null && _pauseSource.IsPaused)
{
await _pauseSource.Token.WaitWhilePausedAsync();
}
//...
}
}
public void Start()
{
//_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, null);
//_task = Task.Factory.StartNew(ExecuteAsync, _cancelSource.Token);
//_task = Task.Run(ExecuteAsync, _cancelSource.Token);
}
UPDATE 2
I think I've narrowed this down, but still not sure about the correct syntax. Would this be the right way to create the task so that the consumer / calling code continues on, with the task spinning-up and starting on a new asynchronous thread?
_task = Task.Run(async () => await ExecuteAsync, _cancelSource.Token);
//**OR**
_task = Task.Factory.StartNew(async () => await ExecuteAsync, _cancelSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Here are some points:
async void methods are only good for asynchronous event handlers (more info). Your async void ExecuteAsync() returns instantly (as soon as the code flow reaches await _pauseSource inside it). Essentially, your _task is in the completed state after that, while the rest of ExecuteAsync will be executed unobserved (because it's void). It may even not continue executing at all, depending on when your main thread (and thus, the process) terminates.
Given that, you should make it async Task ExecuteAsync(), and use Task.Run or Task.Factory.StartNew instead of new Task to start it. Because you want your task's action method be async, you'd be dealing with nested tasks here, i.e. Task<Task>, which Task.Run would automatically unwrap for you. More info can be found here and here.
PauseTokenSource takes the following approach (by design, AFAIU): the consumer side of the code (the one which calls Pause) actually only requests a pause, but doesn't synchronize on it. It will continue executing after Pause, even though the producer side may not have reached the awaiting state yet, i.e. await _pauseSource.Token.WaitWhilePausedAsync(). This may be ok for your app logic, but you should be aware of it. More info here.
[UPDATE] Below is the correct syntax for using Factory.StartNew. Note Task<Task> and task.Unwrap. Also note _task.Wait() in Stop, it's there to make sure the task has completed when Stop returns (in a way similar to Thread.Join). Also, TaskScheduler.Default is used to instruct Factory.StartNew to use the thread pool scheduler. This is important if your create your HighPrecisionTimer object from inside another task, which in turn was created on a thread with non-default synchronization context, e.g. a UI thread (more info here and here).
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
public class HighPrecisionTimer
{
Task _task;
CancellationTokenSource _cancelSource;
public void Start()
{
_cancelSource = new CancellationTokenSource();
Task<Task> task = Task.Factory.StartNew(
function: ExecuteAsync,
cancellationToken: _cancelSource.Token,
creationOptions: TaskCreationOptions.LongRunning,
scheduler: TaskScheduler.Default);
_task = task.Unwrap();
}
public void Stop()
{
_cancelSource.Cancel(); // request the cancellation
_task.Wait(); // wait for the task to complete
}
async Task ExecuteAsync()
{
Console.WriteLine("Enter ExecuteAsync");
while (!_cancelSource.IsCancellationRequested)
{
await Task.Delay(42); // for testing
// DO CUSTOM TIMER STUFF...
}
Console.WriteLine("Exit ExecuteAsync");
}
}
class Program
{
public static void Main()
{
var highPrecisionTimer = new HighPrecisionTimer();
Console.WriteLine("Start timer");
highPrecisionTimer.Start();
Thread.Sleep(2000);
Console.WriteLine("Stop timer");
highPrecisionTimer.Stop();
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}
I'm adding code for running long running task (infinite with cancelation) with internal sub tasks:
Task StartLoop(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(async () => {
while (true)
{
if (cancellationToken.IsCancellationRequested)
break;
await _taskRunner.Handle(cancellationToken);
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
}
},
cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
I have a mostly IO-bound continuous task (a background spellchecker talking to a spellcheck server). Sometimes, this task needs to be put on hold and resumed later, depending on the user activity.
While suspend/resume is essentially what async/await does, I've found little information on how to implement the actual pause/play logic for an asynchronous method. Is there a recommended pattern for this?
I've also looked at using Stephen Toub's AsyncManualResetEvent for this, but thought it might be an overkill.
Updated for 2019, I've recently had a chance to revisit this code, below is complete example as a console app (warning: PauseTokenSource needs good unit testing).
Note, in my case, the requirement was that when the consumer-side code (which requested the pause) would continue, the producer-side code should have already reached the paused state. Thus, by the time the UI is ready to reflect the paused state, all background activity is expected to have been already paused.
using System;
using System.Threading.Tasks;
using System.Threading;
namespace Console_19613444
{
class Program
{
// PauseTokenSource
public class PauseTokenSource
{
bool _paused = false;
bool _pauseRequested = false;
TaskCompletionSource<bool> _resumeRequestTcs;
TaskCompletionSource<bool> _pauseConfirmationTcs;
readonly SemaphoreSlim _stateAsyncLock = new SemaphoreSlim(1);
readonly SemaphoreSlim _pauseRequestAsyncLock = new SemaphoreSlim(1);
public PauseToken Token { get { return new PauseToken(this); } }
public async Task<bool> IsPaused(CancellationToken token = default(CancellationToken))
{
await _stateAsyncLock.WaitAsync(token);
try
{
return _paused;
}
finally
{
_stateAsyncLock.Release();
}
}
public async Task ResumeAsync(CancellationToken token = default(CancellationToken))
{
await _stateAsyncLock.WaitAsync(token);
try
{
if (!_paused)
{
return;
}
await _pauseRequestAsyncLock.WaitAsync(token);
try
{
var resumeRequestTcs = _resumeRequestTcs;
_paused = false;
_pauseRequested = false;
_resumeRequestTcs = null;
_pauseConfirmationTcs = null;
resumeRequestTcs.TrySetResult(true);
}
finally
{
_pauseRequestAsyncLock.Release();
}
}
finally
{
_stateAsyncLock.Release();
}
}
public async Task PauseAsync(CancellationToken token = default(CancellationToken))
{
await _stateAsyncLock.WaitAsync(token);
try
{
if (_paused)
{
return;
}
Task pauseConfirmationTask = null;
await _pauseRequestAsyncLock.WaitAsync(token);
try
{
_pauseRequested = true;
_resumeRequestTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_pauseConfirmationTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
pauseConfirmationTask = WaitForPauseConfirmationAsync(token);
}
finally
{
_pauseRequestAsyncLock.Release();
}
await pauseConfirmationTask;
_paused = true;
}
finally
{
_stateAsyncLock.Release();
}
}
private async Task WaitForResumeRequestAsync(CancellationToken token)
{
using (token.Register(() => _resumeRequestTcs.TrySetCanceled(), useSynchronizationContext: false))
{
await _resumeRequestTcs.Task;
}
}
private async Task WaitForPauseConfirmationAsync(CancellationToken token)
{
using (token.Register(() => _pauseConfirmationTcs.TrySetCanceled(), useSynchronizationContext: false))
{
await _pauseConfirmationTcs.Task;
}
}
internal async Task PauseIfRequestedAsync(CancellationToken token = default(CancellationToken))
{
Task resumeRequestTask = null;
await _pauseRequestAsyncLock.WaitAsync(token);
try
{
if (!_pauseRequested)
{
return;
}
resumeRequestTask = WaitForResumeRequestAsync(token);
_pauseConfirmationTcs.TrySetResult(true);
}
finally
{
_pauseRequestAsyncLock.Release();
}
await resumeRequestTask;
}
}
// PauseToken - consumer side
public struct PauseToken
{
readonly PauseTokenSource _source;
public PauseToken(PauseTokenSource source) { _source = source; }
public Task<bool> IsPaused() { return _source.IsPaused(); }
public Task PauseIfRequestedAsync(CancellationToken token = default(CancellationToken))
{
return _source.PauseIfRequestedAsync(token);
}
}
// Basic usage
public static async Task DoWorkAsync(PauseToken pause, CancellationToken token)
{
try
{
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("Before await pause.PauseIfRequestedAsync()");
await pause.PauseIfRequestedAsync();
Console.WriteLine("After await pause.PauseIfRequestedAsync()");
await Task.Delay(1000);
}
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}", e);
throw;
}
}
static async Task Test(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: " + await pts.IsPaused());
Console.WriteLine("Press enter to resume...");
Console.ReadLine();
Console.WriteLine("Before resume");
await pts.ResumeAsync();
Console.WriteLine("After resume");
}
}
static async Task Main()
{
await Test(CancellationToken.None);
}
}
}
AsyncManualResetEvent is exactly what you need, considering how messy your current code is. But a slightly better solution would be to use another approach from Stephen Toub: PauseToken. It works similarly to AsyncManualResetEvent, except its interface is made specifically for this purpose.
All the other answers seem either complicated or missing the mark when it comes to async/await programming by holding the thread which is CPU expensive and can lead to deadlocks. After lots of trial, error and many deadlocks, this finally worked for my high usage test.
var isWaiting = true;
while (isWaiting)
{
try
{
//A long delay is key here to prevent the task system from holding the thread.
//The cancellation token allows the work to resume with a notification
//from the CancellationTokenSource.
await Task.Delay(10000, cancellationToken);
}
catch (TaskCanceledException)
{
//Catch the cancellation and it turns into continuation
isWaiting = false;
}
}
it is works for me
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTest2
{
class Program
{
static ManualResetEvent mre = new ManualResetEvent(false);
static void Main(string[] args)
{
mre.Set();
Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine("________________");
mre.WaitOne();
}
} );
Thread.Sleep(10000);
mre.Reset();
Console.WriteLine("Task Paused");
Thread.Sleep(10000);
Console.WriteLine("Task Will Resume After 1 Second");
Thread.Sleep(1000);
mre.Set();
Thread.Sleep(10000);
mre.Reset();
Console.WriteLine("Task Paused");
Console.Read();
}
}
}
Ok, maybe this deserves an answer, but I'm not so familiar with C# and I don't have MonoDevelop here, and it's 3 o' clock AM, so please have pity.
I'm suggesting something like this
class Spellchecker
{
private CancellationTokenSource mustStop = null;
private volatile Task currentTask = null;
//TODO add other state variables as needed
public void StartSpellchecker()
{
if (currentTask != null)
{
/*
* A task is already running,
* you can either throw an exception
* or silently return
*/
}
mustStop = new CancellationTokenSource();
currentTask = SpellcheckAsync(mustStop.Token);
currentTask.Start();
}
private async Task SpellcheckAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested))
{
/*
* TODO perform spell check
* This method must be the only one accessing
* the spellcheck-related state variables
*/
}
currentTask = null;
}
public async Task StopSpellchecker()
{
if (currentTask == null)
{
/*
* There is no task running
* you can either throw an exception
* or silently return
*/
}
else
{
/*
* A CancelAfter(TimeSpan) method
* is also available, which might interest you
*/
mustStop.Cancel();
//Remove the following lines if you don't want to wait for the task to actually stop
var task = currentTask;
if (task != null)
{
await task;
}
}
}
}