How to poll from worker thread for cancellation - c#

I have a UI which spawns off a background worker thread which performs a complex tree of tasks and sub-tasks that takes about a minute to complete.
A requirement is that the background worker task must be capable of being cancelled once it has begun.
At the moment my solution is naive and makes the code a mess. When a cancel button is pressed in the UI, a cancel token is set. The worker thread periodically (between tasks) polls this token and if it is set, it exits:
void ThreadWorkerHandler(CancelToken cancelToken)
{
DoTask1(cancelToken);
if (cancelToken.IsSet)
return;
DoTask2(cancelToken);
if (cancelToken.IsSet)
return;
DoTask3(cancelToken);
if (cancelToken.IsSet)
return;
DoTask4(cancelToken);
}
void DoTask2(CancelToken cancelToken)
{
DoSubTask2a();
if (cancelToken.IsSet)
return;
DoSubTask2b();
if (cancelToken.IsSet)
return;
DoSubTask2c();
if (cancelToken.IsSet)
return;
}
Is there a better solution? I was toying for something like a SoLongAs statement that would automatically pepper the checks in and automatically and raise an internal exception if the condition was met, which would be internally caught at the end of the loop, eg:
void ThreadWorkerHandler(CancelToken cancelToken)
{
SoLongAs (canelToken.IsSet == false)
{
DoTask1(cancelToken);
DoTask2(cancelToken);
DoTask3(cancelToken);
DoTask4(cancelToken);
}
}
But I imagine that wouldn't work for some reason, also more importantly I doubt something like this actually exists. If not is there a better way to handle this scenario than I am currently using? Thanks.

If you have a collection of delegates that represent your work you can get something that looks pretty close to your code snippet. It has a bit more overhead than your intented syntax, but the key point is that it's a constant overhead, rather than a per-line overhead.
List<Action> actions = new List<Action>()
{
()=> DoTask1(cancelToken),
()=> DoTask2(cancelToken),
()=> DoTask3(cancelToken),
()=> DoTask4(cancelToken),
};
foreach(var action in actions)
{
if (!cancelToken.IsSet)
action();
}

You can use CancellationToken.ThrowIfCancellationRequested(). this will throw exception if token was set.
Also consider using TPL Tasks. All subtasks can be chained one after another with same CancellationToken, this would simplify your code, as TPL framework would take care about checking Token state before invoking continuation.
Your code would looks like this:
Task.Factory.StartNew(DoTask1, cancelationToken)
.ContinueWith(t => DoTask2(), cancelationToken)
.ContinueWith(t => DoTask3(), cancelationToken)
.ContinueWith(t => DoTask4(), cancelationToken)
Note this solution supposing that DoTask<i> will not throw other exceptions except OperationCanceledException.
Note2 you don't have to call ThrowIfCancellationRequested() inside Tasks/subTasks body. TPL will automatically check token state before invoking any continuations. But you can use this method to interrupt execution of task/subtask.

Servy's idea is very good. I'm just stealing it (with all credit to him!) and demonstrating how to use it with an extension method for List<Action>. I'll fully understand anyone that thinks this is "too cute", but I think it has a certain elegance.
Here's an exerpt that show how you can use the extension method. The extension takes a list of Action delegates and runs each one in turn until finished or cancelled, as per Servy's idea.
private static bool test(CancellationToken cancelToken)
{
return new List<Action>
{
doTask1,
doTask2,
doTask3,
doTask4,
() => Console.WriteLine("Press a key to exit.")
}
.Run(cancelToken);
}
And here's the entire sample:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
internal class Program
{
private static void Main(string[] args)
{
CancellationTokenSource cancelSource = new CancellationTokenSource();
Console.WriteLine("Press any key to interrupt the work.");
var work = Task<bool>.Factory.StartNew(() => test(cancelSource.Token));
Console.ReadKey();
cancelSource.Cancel();
Console.WriteLine(work.Result ? "Completed." : "Interrupted.");
}
private static bool test(CancellationToken cancelToken)
{
return new List<Action>
{
doTask1,
doTask2,
doTask3,
doTask4,
() => Console.WriteLine("Press a key to exit.")
}
.Run(cancelToken);
}
private static void doTask1()
{
Console.WriteLine("Task 1 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
private static void doTask2()
{
Console.WriteLine("Task 2 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
private static void doTask3()
{
Console.WriteLine("Task 3 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
private static void doTask4()
{
Console.WriteLine("Task 4 Working...");
Thread.Sleep(1000);
Console.WriteLine("...did some work.");
}
}
public static class EnumerableActionExt
{
public static bool Run(this IEnumerable<Action> actions, CancellationToken cancelToken)
{
foreach (var action in actions)
{
if (!cancelToken.IsCancellationRequested)
{
action();
}
else
{
return false;
}
}
return true;
}
}
}

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.

Mutex does not block async method

I have a code that looks a bit like that. I have a method do_it that does the work and uses some resources that must be protected by a mutex. To do it, I tried using a mutex before calling it, to force other tasks to block until the work is done. But the mutex does not block the second task, and both runs in paralell, I would like to know why, and what is the best way to do it.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace teste_threads
{
class test_mutex
{
Mutex mut = new Mutex();
public async Task first()
{
mut.WaitOne();
await do_it();
mut.ReleaseMutex();
}
public async Task second()
{
mut.WaitOne();
await do_it();
mut.ReleaseMutex();
}
public async Task do_it()
{
await Task.Run(() => doSomething());
}
private void doSomething()
{
Console.WriteLine("doSomething starting");
Thread.Sleep(1000);
Console.WriteLine("doSomething ending");
}
}
class Program
{
static test_mutex test = new test_mutex();
static void Main(string[] args)
{
test.first();
Thread.Sleep(100);
test.second();
Thread.Sleep(3000);
}
}
}
I was expecting that the output to this code would be:
doSomething starting
doSomething ending
doSomething starting
doSomething ending
but instead, I get:
doSomething starting
doSomething starting
doSomething ending
doSomething ending
that means the mutex is not blocking when second() is called.
What is wrong with that mutex?
Mutexes - and other synchronous synchronization primitives - cannot be used with asynchronous code. You need asynchronous synchronization primitives.
There is one asynchronous synchronization primitive built-in: SemaphoreSlim, which can be used as an asynchronous mutex as such:
SemaphoreSlim mut = new SemaphoreSlim(1);
public async Task first()
{
await mut.WaitAsync();
try
{
await do_it();
}
finally
{
mut.Release();
}
}
public async Task second()
{
await mut.WaitAsync();
try
{
await do_it();
}
finally
{
mut.Release();
}
}
The problem is that both first and second methods are running in the same thread, so your mutex is basicaly not blocking at all. Try to remove the ReleaseMutex() calls and it will work the same way proving that there are no 2 threads running the code so the Mutex usage is useless.

Do Multi delegate Async Actions Only work with Begin Invoke

I'm trying to understand async actions and I'm a bit confused.
Actions are just glorified Delegates. Given the Actions
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () => { await File.AppendAllLinesAsync("C:/Test.Txt",
new[] { "Async File Operation" });
};
How can we invoke this async seeing as one of the delegates is async and the other is not. I've seen some extension methods in other SO answers simplified for the example would look like so:
public static void InvokeAsync(this Action action, AsyncCallback ar, object userObject = null)
{
var listeners = action.GetInvocationList();
foreach (var t in listeners)
{
var handler = (Action)t;
handler.BeginInvoke(ar, userObject);
}
}
I'm concerned if this even works because it looks like it invokes your callback for each listener which doesn't make sense.
I've only been using async with the more friendly version async/await so I do not understand this syntax as much. (I'm assuming the callback would be everything after the await and the userObject is equivalent to the dreadful SyncronizationContext that causes deadlocks if when calling sync without ConfigureAwait(false), but that is just a guess)
This is syntax inconvenient so I would perfer to use async await syntax, since async/await is called using duck-typing. I've read a blog about using async with delegates which for the example
public static class DelegateExtensions
{
public static TaskAwaiter GetAwaiter(this Action action)
{
Task task = new Task(action);
task.Start();
return task.GetAwaiter();
}
}
This too concerns me for a few reason, this looks much like an anti pattern.
Isn't this just creating a task which will run my action synchronous on a seperate thread? I also don't see this run through the invocation list.
Are either of these methods proper for invoking run delegates asynchronously?
Is there a way I can invoke an async delegate with the await syntax while still fully leveraging async?
What is the proper way to invoke async delegates with multiple functions in the invocation list?
I think Eric Lippert's comment have clarified the situation more than I could ever.
Overall, if you need to act on the return type of a method, you shouldn't use multicast delegates. If you still have to, at least use a Func<Task> signature, then you can iterate on each individual delegate using GetInvocationList, as explained here.
But would it be really impossible to work your way out of a multicast delegate with async void method?
It turns out that you can be notified of beginning and end of async void methods by using a custom synchronization context and overriding the OperationStarted and OperationCompleted methods. We can also override the Post method to set the synchronization context of child operations, to capture subsequent async void calls.
Piecing it together, you could come with something like:
class Program
{
static async Task Main(string[] args)
{
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () =>
{
Callback();
await Task.Delay(1000);
Console.WriteLine("Async");
};
await AwaitAction(act);
Console.WriteLine("Done");
Console.ReadLine();
}
static async void Callback()
{
await Task.Delay(2000);
Console.WriteLine("Async2");
}
static Task AwaitAction(Action action)
{
var delegates = action.GetInvocationList();
var oldSynchronizationContext = SynchronizationContext.Current;
var asyncVoidSynchronizationContext = new AsyncVoidSynchronizationContext();
try
{
SynchronizationContext.SetSynchronizationContext(asyncVoidSynchronizationContext);
var tasks = new Task[delegates.Length];
for (int i = 0; i < delegates.Length; i++)
{
((Action)delegates[i]).Invoke();
tasks[i] = asyncVoidSynchronizationContext.GetTaskForLastOperation();
}
return Task.WhenAll(tasks);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
}
public class AsyncVoidSynchronizationContext : SynchronizationContext
{
private TaskCompletionSource<object> _tcs;
private Task _latestTask;
private int _operationCount;
public Task GetTaskForLastOperation()
{
if (_latestTask != null)
{
var task = _latestTask;
_latestTask = null;
return task;
}
return Task.CompletedTask;
}
public override void Post(SendOrPostCallback d, object state)
{
Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
d(state);
});
}
public override void OperationStarted()
{
if (Interlocked.Increment(ref _operationCount) == 1)
{
// First operation
_tcs = new TaskCompletionSource<object>();
_latestTask = _tcs.Task;
}
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
{
// Last operation
_tcs.TrySetResult(null);
}
base.OperationCompleted();
}
}
The output would be:
Sync
Async
Async2
Done
Of course, this code is provided just for recreational purpose. There's plenty of limitations, such as the fact the fact that it wouldn't work as-is if you're already using a synchronization context (such as the WPF one). I'm also certain that it has a few subtle bugs and concurrency issues here and there.

How to assert a task was restarted at least once

I am test driving a class that gets injected with a bunch of work tasks, runs the asynchronously and restarts them when completed until told to halt all tasks.
Since I am doing test first I needed to write a test that forces me to write the restart logic, and I have kind of successfully done this, but I don't think I did it very well.
Test code: (FakeTask is basically a test spy that keeps track on whether it was called and how many times)
[Fact]
public async void Start_GivenTask_RerunsTaskUntilStopped()
{
var agent = CreateKlarnaAgent();
var fakeTask = DoNothingTask();
agent.Start(fakeTask);
Thread.Sleep(500);
await agent.Stop();
Assert.True(fakeTask.TimesRun > 1);
}
(Relevant) production code:
public void Start(params IWorkTask[] workTasks)
{
_logWriter.Debug("Starting...");
_tasks = workTasks
.Select(workTask => workTask.DoWork().ContinueWith(task => OnTaskComplete(task, workTask)))
.ToArray();
}
private void OnTaskComplete(Task completedTask, IWorkTask workTask)
{
if (completedTask.IsFaulted)
{
foreach (var exception in completedTask.Exception.InnerExceptions)
{
_logWriter.Error("Unhandled exception thrown!", exception);
}
}
else workTask.DoWork().ContinueWith(task => OnTaskComplete(task, workTask));
}
public Task Stop()
{
return Task.WhenAll(_tasks)
.ContinueWith(t => { _logWriter.Debug("Stopped"); });
}
The test is now really depending on a race condition and it doesn't feel like a unit test at all. How can I get rid of the Thread.Sleep(500) call? Or is this simply something I should test in an integration test?
On a side note, I recommend against writing "task runners" in general, and also recommend against ContinueWith in particular since it is such a dangerous API.
In my opinion, the "repeat forever until canceled" logic is far more clearly expressed using a loop for "repeat" and a cancellation token for "canceled":
static async Task WorkAsync(Func<Task> doWork, CancellationToken token)
{
while (true)
{
await doWork();
token.ThrowIfCancellationRequested();
}
}
However, that said, if you want to unit test your "task runner" as-is, you'll need to make your FakeTask more intelligent. For example, you could have it set a signal when it reaches a given count and have your unit test wait on that:
class FakeTask : IWorkTask
{
private readonly TaskCompletionSource<object> _done = new TaskCompletionSource<object>();
public Task Done { get { return _done.Task; } }
public Task DoWork()
{
++TimesRun;
if (TimesRun > 1)
_done.TrySetResult(null);
return Task.CompletedTask;
}
}
[Fact]
public async Task Start_GivenTask_RerunsTaskUntilStopped()
{
var agent = CreateKlarnaAgent();
var fakeTask = DoNothingTask();
agent.Start(fakeTask);
await fakeTask.Done;
await agent.Stop();
Assert.True(fakeTask.TimesRun > 1); // spurious test at this point
}

Run work on specific thread

I would like to have one specific thread, queue for Tasks and process tasks in that separate thread. The application would make Tasks based on users usage and queue them into task queue. Then the separate thread processes the tasks. It is vital to keep the thread alive and use it for processing queued tasks even if queue is empty.
I have tried several implementations of TaskScheduler with BlockingCollection and limit the concurrency to only one thread but it seems the Thread gets disposed when queue gets empty and the Task is processed by other thread.
Can you please at least refer me to some sources how to achieve this goal?
tl;dr
Trying to limit one specific thread to process tasks which are dynamically added to the queue.
Edit1:
This is experimental web app that uses WCF and .NET framework 4.6. In the WCF library, I am trying to implement this behaviour with one thread processing tasks. This one thread must init prolog using external dll library and then do work with prolog. If other thread is used in process, library throws AccessViolationException. I've done some research and this is most probably because of badly managed threading in that library. I had implementation where I had locks everywhere and it worked. I am now trying to reimplement and make it asynchronous so I don't block the main thread with locking.
I am not at my computer but I provide some code when I get home later today.
Your approach seems fine, so you probably just made some tiny stupid mistake.
It's actually pretty easy to make a simple custom TaskScheduler. For your case:
void Main()
{
var cts = new CancellationTokenSource();
var myTs = new SingleThreadTaskScheduler(cts.Token);
myTs.Schedule(() =>
{ Print("Init start"); Thread.Sleep(1000); Print("Init done"); });
myTs.Schedule(() => Print("Work 1"));
myTs.Schedule(() => Print("Work 2"));
myTs.Schedule(() => Print("Work 3"));
var lastOne = myTs.Schedule(() => Print("Work 4"));
Print("Starting TS");
myTs.Start();
// Wait for all of them to complete...
lastOne.GetAwaiter().GetResult();
Thread.Sleep(1000);
// And try to schedule another
myTs.Schedule(() => Print("After emptied")).GetAwaiter().GetResult();
// And shutdown; it's also pretty useful to have the
// TaskScheduler return a "complete task" to await
myTs.Complete();
Print("On main thread again");
}
void Print(string str)
{
Console.WriteLine("{0}: {1}", Thread.CurrentThread.ManagedThreadId, str);
Thread.Sleep(100);
}
public sealed class SingleThreadTaskScheduler : TaskScheduler
{
[ThreadStatic]
private static bool isExecuting;
private readonly CancellationToken cancellationToken;
private readonly BlockingCollection<Task> taskQueue;
public SingleThreadTaskScheduler(CancellationToken cancellationToken)
{
this.cancellationToken = cancellationToken;
this.taskQueue = new BlockingCollection<Task>();
}
public void Start()
{
new Thread(RunOnCurrentThread) { Name = "STTS Thread" }.Start();
}
// Just a helper for the sample code
public Task Schedule(Action action)
{
return
Task.Factory.StartNew
(
action,
CancellationToken.None,
TaskCreationOptions.None,
this
);
}
// You can have this public if you want - just make sure to hide it
private void RunOnCurrentThread()
{
isExecuting = true;
try
{
foreach (var task in taskQueue.GetConsumingEnumerable(cancellationToken))
{
TryExecuteTask(task);
}
}
catch (OperationCanceledException)
{ }
finally
{
isExecuting = false;
}
}
// Signaling this allows the task scheduler to finish after all tasks complete
public void Complete() { taskQueue.CompleteAdding(); }
protected override IEnumerable<Task> GetScheduledTasks() { return null; }
protected override void QueueTask(Task task)
{
try
{
taskQueue.Add(task, cancellationToken);
}
catch (OperationCanceledException)
{ }
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// We'd need to remove the task from queue if it was already queued.
// That would be too hard.
if (taskWasPreviouslyQueued) return false;
return isExecuting && TryExecuteTask(task);
}
}
It's pretty easy to modify this to give you full control on where the task scheduler is actually executing the task - in fact, I've adapted this from a previous task scheduler I've used which simply had the RunOnCurrentThread method public.
For your case, where you always want to stick to just the one thread, the approach in SingleThreadTaskScheduler is probably better. Although this also has its merits:
// On a new thread
try
{
InitializeProlog();
try
{
myTs.RunOnCurrentThread();
}
finally
{
ReleaseProlog();
}
}
catch (Exception ex)
{
// The global handler
}

Categories