How to dispatch every previous Task that was created by particular event - c#

I have an event in my WPF .NET 4.5 application that can be triggered up to 10 times per second, the event is computing data that is not required that often so I'm looking for a way to remove unnecessary computation stress and call the method if event wasn't triggered for 3 seconds. I thought about using async functionality like that:
private async Task DoWork()
{
await Task.Delay(3000);
...
}
private void Event()
{
Task.Run(() => DoWork());
}
I'm not really sure about how to elegantly handle disposing of unwanted Tasks, ie. when user trigger the event, every task that was created by that event should be terminated and it should be as fast as possible. I've tried with CancellationToken but I'm not sure this is the right approach for my case

You can use CancellationTokenSource to let the code inside task know that it is canceled:
private CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource ();
Let's change:
private async Task DoWork()
{
await Task.Delay(3000);
...
}
To:
private async Task DoWork(int timeout = 3000)
{
await Task.Delay(timeout, CancellationTokenSource.Token);
if(!CancellationTokenSource.Token.IsCancellationRequested)
{
...
}
}
Now we can cancel our task if required:
CancellationTokenSource.Cancel();
Task.Delay will observe the CancellationToken and if it is in a Canceled state it will abort the task execution. Later in code we check whether we need to do anything or not.

That was my solution based on #Fabjan answer.
private static void RunSingleTask(ref CancellationTokenSource cts, int delay, Action func)
{
if (cts != null)
{
cts.Cancel();
cts.Dispose();
}
cts = new CancellationTokenSource();
var token = cts.Token;
Task.Run(async () =>
{
try
{
await Task.Delay(delay, token);
}
catch (TaskCanceledException)
{
return;
}
await Application.Current.Dispatcher.BeginInvoke(func);
});
}

Related

How to wait on AutoResetEvent and be able to terminate the wait after cancel is called?

I need to be able to wait for an event, but also be able to terminate the wait after cancel is called.
I use AutoResetEvent as a way to wait on a signal to continue the work. To be able to cancel the wait, I came up with two solutions:
Register a delegate with CancellationToken.Register that will set AutoResetEvent.
Using TaskCompletionSource. But, since I cannot reuse TaskCompletionSource, I came up with the solution to queue new TaskCompletionSource each time a new event is fired.
Are these proper solutions or there are more elegant ways to do this?
Solution 1
class MyClass
{
AutoResetEvent _dataArrivedSignal = new AutoResetEvent (false);
public Task RunAsync(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
cancellationToken.Register(() => _dataArrivedSignal.Set());
while(condition)
{
DoSomeWork();
_dataArrivedSignal.WaitOne();
cancellationToken.ThrowIfCancellationRequested();
}
}
}
private void OnDataArrived(EventArgs args)
{
_dataArrivedSignal.Set();
}
}
Solution 2
class MyClass
{
ConcurrentQueue<TaskCompletionSource> _awaiters = new ConcurrentQueue<TaskCompletionSource>();
TaskCompletionSource _waiter;
public MyClass3()
{
_waiter = new TaskCompletionSource();
_awaiters.Enqueue(_waiter);
}
public Task RunAsync(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
while(condition)
{
DoSomeWork();
_awaiters.TryDequeue(out TaskCompletionSource waiter);
waiter.Task.Wait(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
}
}
}
private void OnDataArrived(EventArgs args)
{
var newWaiter = new TaskCompletionSource();
_awaiters.Enqueue(newWaiter);
_waiter.SetResult();
_waiter = newWaiter;
}
}
Yes, there is more elegant way. Note that AutoResetEvent inherits from WaitHandle. CancellationToken in turn has property WaitHandle, described as:
Gets a WaitHandle that is signaled when the token is canceled.
Then, WaitHandle has static method WaitAny which accepts array of wait handles and returns an index in that array of first wait handle that was signaled.
So to achieve what you want - use:
public Task RunAsync(CancellationToken cancellationToken) {
return Task.Factory.StartNew(() => {
while (condition) {
DoSomeWork();
int signaled = WaitHandle.WaitAny(new[] { _dataArrivedSignal, cancellationToken.WaitHandle });
if (signaled == 0) {
// your _dataArrivedSignal was signaled
}
else {
// cancellation token was signaled
}
}
});
}
WaitAny can also accept timeout in case you actually use WaitOne with timeout in real code.

Await long-running task on original context

I have some objects that start long-running background work on construction and would like to await their completion on IAsyncDisposable.
I would like to run this work on the thread pool. I am trying to figure out the safest way to do this while avoiding deadlocks. I cannot figure out how to use JoinableTaskFactory and/or JoinableContext to do this.
using Microsoft.VisualStudio.Threading;
public class Worker : System.IAsyncDisposable
{
private CancellationTokenSource _cts;
private Task _work;
private JoinableTask _workSafe;
public Worker()
{
_cts = new();
// goes on the thread pool. But no way to join/await on that thread later?
_work = Task.Run(() => DoWorkAsync(_cts.Token));
// using the library I can await on same thread later. But it
// executes on the same thread as the constructor (undesirable)
// according to the documentation
var ctx = new JoinableTaskContext();
_workSafe = ctx.Factory.RunAsync(() => DoWorkAsync(_cts.Token), JoinableTaskCreationOptions.LongRunning);
}
private async Task DoWorkAsync(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(50);
}
}
public async ValueTask DisposeAsync()
{
_cts.Cancel();
// VS says this is unsafe
await _work;
// But this is safe
await _workSafe;
_cts.Dispose();
}
}

Does cancelling a CancellationToken cause a CancellationToken Exception?

I have this code and I would like to get some clarification on the use of the CancellationToken.
I read this question about the difference between using a cancellation token and a flag:
Difference between CancellationTokenSource and exit flag for Task loop exit
One thing I noticed is that it mentions nothing about Exceptions. So here's my question. If the Disappearing() method is called then will this cause a TaskCanceledException() to occur and would this be a good reason to use the CancellationToken instead of a flag?
public partial class PhrasesFrame : Frame
{
CancellationTokenSource cts = new CancellationTokenSource();
public PhrasesFrame(PhrasesPage phrasesPage)
{
Device.BeginInvokeOnMainThread(() => ShowCards(cts.Token).ContinueWith((arg) => { }));
}
public void Disappearing()
{
cts.Cancel();
}
public async Task ShowCards(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await PickCard();
}
}
public async Task PickCard()
{
await ShowCard();
}
private async Task ShowCard()
{
await ShowPhrase();
await ShowDetail();
}
private async Task ShowPhrase()
{
while (App.pauseCard || timer1Seconds > 0)
{
try
{
await Task.Delay(1000, tokenSource1.Token);
}
catch (TaskCanceledException)
{
// do action
break;
}
}
CancellationTokenSource.Cancel by itself doesn't throw such exception, but it "moves" all related cancellation tokens to cancelled state. When some code notifies that cancellation token is now in cancelled state - it might throw such exception. If look at your example, this part will not throw such exception:
public async Task ShowCards(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await PickCard();
}
}
Because you just don't throw it in this block. If however you instead did something like this:
public async Task ShowCards(CancellationToken ct)
{
while (true)
{
ct.ThrowIfCancellationRequested();
await PickCard();
}
}
Then exception will be thrown, because, well, you throw it almost explicitly.
Now if look at another method from your example:
private async Task ShowPhrase()
{
while (App.pauseCard || timer1Seconds > 0)
{
try
{
await Task.Delay(1000, tokenSource1.Token);
}
catch (TaskCanceledException)
{
// do action
break;
}
}
}
If you were awaiting Task.Delay(1000, tokenSource1.Token); and then cancel tokenSource1 - then TaskCancelledException will indeed be thrown immediately, without waiting for the whole duration of Task.Delay. This is something you cannot easily achieve with just a boolean flag. If you used Thread.Sleep(1000) and boolean flag - change to that flag won't be noticed until whole duration of sleep is finished.
So to answer your question: in your example exception might or might not be thrown, depending on what part of code is currently executing at the moment you cancel your CancellationTokenSource (I assume that using two cancellation token sources with names cts and tokenSource1 is just a typo in your code, but if it's real code - then such exception cannot be thrown at all, because you cancel cts but Task.Delay waits on tokenSource1).

How to make a nonblocking wait handle?

Essentially, what I'm doing is creating a web server to handle an API call, and then when it's done continue the method execution, so essentially:
new WebServer(myAutoResetEvent);
myAutoResetEvent.WaitOne();
However, this blocks the thread until then. Is there any way to make this async? Is it fine just to wrap it in a await Task.Run() call, i.e. await Task.Run(() => myAutoResetEvent.WaitOne())?
Thanks!
Normally, the WebServer ctor should not do anything interesting. There should be a Task WebServer.RunAsync function that runs the server. You can then use the resulting task to synchronize and coordinate.
If you don't want that you can use a TaskCompletionSource<object> as a one-shot async-ready event.
I believe the ThreadPool class has a way to efficiently wait for a WaitHandle to be set but that's a worse solution.
You should not block ThreadPool threads, this is a quick way to lead to ThreadPool starvation, instead there is a provided method to asynchronously wait for WaitHandle instances, this is called ThreadPool.RegisterWaitForSingleObject.
By using ThreadPool.RegisterWaitForSingleObject a callback is registered to be invoked when the WaitHandle is available, unfortunately this is not async/await compatible out of the box, a full implementation which makes this async/await compatible is as follows:
public static class WaitHandleExtensions
{
public static Task WaitOneAsync(this WaitHandle waitHandle, CancellationToken cancellationToken)
{
return WaitOneAsync(waitHandle, Timeout.Infinite, cancellationToken);
}
public static async Task<bool> WaitOneAsync(this WaitHandle waitHandle, int timeout, CancellationToken cancellationToken)
{
// A Mutex can't use RegisterWaitForSingleObject as a Mutex requires the wait and release to be on the same thread
// but RegisterWaitForSingleObject acquires the Mutex on a ThreadPool thread.
if (waitHandle is Mutex)
throw new ArgumentException(StringResources.MutexMayNotBeUsedWithWaitOneAsyncAsThreadIdentityIsEnforced, nameof(waitHandle));
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<bool>();
var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, OnWaitOrTimerCallback, tcs, timeout, true);
var cancellationCallback = BuildCancellationCallback(rwh, tcs);
using (cancellationToken.Register(cancellationCallback))
{
try
{
return await tcs.Task.ConfigureAwait(false);
}
finally
{
rwh.Unregister(null);
}
}
}
private static Action BuildCancellationCallback(RegisteredWaitHandle rwh, TaskCompletionSource<bool> tcs)
{
return () =>
{
if (rwh.Unregister(null))
{
tcs.SetCanceled();
}
};
}
private static void OnWaitOrTimerCallback(object state, bool timedOut)
{
var taskCompletionSource = (TaskCompletionSource<bool>)state;
taskCompletionSource.SetResult(!timedOut);
}
}
The only limitation is that this cannot be used with a Mutex.
This can be used like so:
await myAutoResetEvent.WaitOneAsync(cancellationToken).ConfigureAwait(false);
Another approach to consider would be to use HttpSelfHostServer (System.Web.Http.SelfHost.dll) and leaving all of the threading details to its implementation.
var config = new HttpSelfHostConfiguration("http://localhost:9999");
var tcs = new TaskCompletionSource<Uri>();
using (var server = new HttpSelfHostServer(config, new MessageHandler(tcs)))
{
await server.OpenAsync();
await tcs.Task;
await server.CloseAsync();
}
return tcs.Task.Result;
class MessageHandler : HttpMessageHandler
{
private readonly TaskCompletionSource<Uri> _task;
public MessageHandler(TaskCompletionSource<Uri> task)
{
_task = task;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_task.SetResult(request.RequestUri);
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}

Why isn't Task.WhenAll working?

Here's some code I'm working with, that fires of three tasks. My Stop is called, and from the output, it looks as though the tasks finish after Stop returns, which isn't what I'd expect.
This is Visual Studio 2013 with .NET 4.5.1
I tried the same using AutoResetEvent which worked as expected.
Here's the output from this:
Three tasks without auto reset
Starting...
FIRST
SECOND
THIRD
Running...
FIRST
FIRST
FIRST
SECOND
FIRST
FIRST
THIRD
...
SECOND
FIRST
Stopping...
THIRD
All done!
First done
Second done
Third done
What I'd expect to see is:
First done
Second done
Third done
All done!
Here's the code (I even added locking around the Console output, but it made no difference):
public class ThreeTasksWithoutAutoResetEvent
{
private CancellationTokenSource cancellation;
private Task[] tasks;
private object obj = new object();
public ThreeTasksWithoutAutoResetEvent()
{
Console.WriteLine("Three tasks without auto reset");
cancellation = new CancellationTokenSource();
tasks = new Task[3];
Message("Starting...");
Start();
Message("Running...");
Thread.Sleep(3000);
Message("Stopping...");
Stop();
Message("All done!");
}
private void Start()
{
tasks[0] = this.First(cancellation.Token);
tasks[1] = this.Second(cancellation.Token);
tasks[2] = this.Third(cancellation.Token);
}
private async void Stop()
{
cancellation.Cancel();
await Task.WhenAll(tasks);
}
private async Task First(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("FIRST");
Thread.Sleep(100);
}
Message("First done");
}, token);
}
private async Task Second(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("SECOND");
Thread.Sleep(300);
}
Message("Second done");
}, token);
}
private async Task Third(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("THIRD");
Thread.Sleep(500);
}
Message("Third done");
}, token);
}
private void Message(string message)
{
lock (obj)
{
Console.WriteLine(message);
}
}
}
Because you're not waiting for Stop method to finish. You can't wait for an async void method. You need return a Task, so that the caller can wait/await for it to complete.
private Task Stop()
{
cancellation.Cancel();
return Task.WhenAll(tasks);
}
Then you can call Stop().Wait(); instead of Stop. That will wait for WhenAll to complete.

Categories