When should TaskCompletionSource<T> be used? - c#

AFAIK, all it knows is that at some point, its SetResult or SetException method is being called to complete the Task<T> exposed through its Task property.
In other words, it acts as the producer for a Task<TResult> and its completion.
I saw here the example:
If I need a way to execute a Func<T> asynchronously and have a Task<T>
to represent that operation.
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Which could be used if I didn’t have Task.Factory.StartNew -
But I do have Task.Factory.StartNew.
Question:
Can someone please explain by example a scenario related directly to TaskCompletionSource
and not to a hypothetical situation in which I don't have Task.Factory.StartNew?

I mostly use it when only an event based API is available (for example Windows Phone 8 sockets):
public Task<Args> SomeApiWrapper()
{
TaskCompletionSource<Args> tcs = new TaskCompletionSource<Args>();
var obj = new SomeApi();
// will get raised, when the work is done
obj.Done += (args) =>
{
// this will notify the caller
// of the SomeApiWrapper that
// the task just completed
tcs.SetResult(args);
}
// start the work
obj.Do();
return tcs.Task;
}
So it's especially useful when used together with the C#5 async keyword.

In my experiences, TaskCompletionSource is great for wrapping old asynchronous patterns to the modern async/await pattern.
The most beneficial example I can think of is when working with Socket. It has the old APM and EAP patterns, but not the awaitable Task methods that TcpListener and TcpClient have.
I personally have several issues with the NetworkStream class and prefer the raw Socket. Being that I also love the async/await pattern, I made an extension class SocketExtender which creates several extension methods for Socket.
All of these methods make use of TaskCompletionSource<T> to wrap the asynchronous calls like so:
public static Task<Socket> AcceptAsync(this Socket socket)
{
if (socket == null)
throw new ArgumentNullException("socket");
var tcs = new TaskCompletionSource<Socket>();
socket.BeginAccept(asyncResult =>
{
try
{
var s = asyncResult.AsyncState as Socket;
var client = s.EndAccept(asyncResult);
tcs.SetResult(client);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}, socket);
return tcs.Task;
}
I pass the socket into the BeginAccept methods so that I get a slight performance boost out of the compiler not having to hoist the local parameter.
Then the beauty of it all:
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610));
listener.Listen(10);
var client = await listener.AcceptAsync();

To me, a classic scenario for using TaskCompletionSource is when it's possible that my method won't necessarily have to do a time consuming operation. What it allows us to do is to choose the specific cases where we'd like to use a new thread.
A good example for this is when you use a cache. You can have a GetResourceAsync method, which looks in the cache for the requested resource and returns at once (without using a new thread, by using TaskCompletionSource) if the resource was found. Only if the resource wasn't found, we'd like to use a new thread and retrieve it using Task.Run().
A code example can be seen here: How to conditionally run a code asynchonously using tasks

In this blog post, Levi Botelho describes how to use the TaskCompletionSource to write an asynchronous wrapper for a Process such that you can launch it and await its termination.
public static Task RunProcessAsync(string processPath)
{
var tcs = new TaskCompletionSource<object>();
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo(processPath)
{
RedirectStandardError = true,
UseShellExecute = false
}
};
process.Exited += (sender, args) =>
{
if (process.ExitCode != 0)
{
var errorMessage = process.StandardError.ReadToEnd();
tcs.SetException(new InvalidOperationException("The process did not exit correctly. " +
"The corresponding error message was: " + errorMessage));
}
else
{
tcs.SetResult(null);
}
process.Dispose();
};
process.Start();
return tcs.Task;
}
and its usage
await RunProcessAsync("myexecutable.exe");

It looks like no one mentioned, but I guess unit tests too can be considered real life enough.
I find TaskCompletionSource to be useful when mocking a dependency with an async method.
In actual program under test:
public interface IEntityFacade
{
Task<Entity> GetByIdAsync(string id);
}
In unit tests:
// set up mock dependency (here with NSubstitute)
TaskCompletionSource<Entity> queryTaskDriver = new TaskCompletionSource<Entity>();
IEntityFacade entityFacade = Substitute.For<IEntityFacade>();
entityFacade.GetByIdAsync(Arg.Any<string>()).Returns(queryTaskDriver.Task);
// later on, in the "Act" phase
private void When_Task_Completes_Successfully()
{
queryTaskDriver.SetResult(someExpectedEntity);
// ...
}
private void When_Task_Gives_Error()
{
queryTaskDriver.SetException(someExpectedException);
// ...
}
After all, this usage of TaskCompletionSource seems another case of "a Task object that does not execute code".

TaskCompletionSource is used to create Task objects that don't execute code.
In real world scenarios, TaskCompletionSource is ideal for I/O bound operations. This way, you get all the benefits of tasks (e.g. return values, continuations, etc) without blocking a thread for the duration of the operation. If your "function" is an I/O bound operation, it isn't recommended to block a thread using a new Task. Instead, using TaskCompletionSource, you can create a slave task to just indicate when your I/O bound operation finishes or faults.

There's a real world example with a decent explanation in this post from the "Parallel Programming with .NET" blog. You really should read it, but here's a summary anyway.
The blog post shows two implementations for:
"a factory method for creating “delayed” tasks, ones that won’t
actually be scheduled until some user-supplied timeout has occurred."
The first implementation shown is based on Task<> and has two major flaws. The second implementation post goes on to mitigate these by using TaskCompletionSource<>.
Here's that second implementation:
public static Task StartNewDelayed(int millisecondsDelay, Action action)
{
// Validate arguments
if (millisecondsDelay < 0)
throw new ArgumentOutOfRangeException("millisecondsDelay");
if (action == null) throw new ArgumentNullException("action");
// Create a trigger used to start the task
var tcs = new TaskCompletionSource<object>();
// Start a timer that will trigger it
var timer = new Timer(
_ => tcs.SetResult(null), null, millisecondsDelay, Timeout.Infinite);
// Create and return a task that will be scheduled when the trigger fires.
return tcs.Task.ContinueWith(_ =>
{
timer.Dispose();
action();
});
}

This may be oversimplifying things but the TaskCompletion source allows one to await on an event. Since the tcs.SetResult is only set once the event occurs, the caller can await on the task.
Watch this video for more insights:
http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Lucian03-TipsForAsyncThreadsAndDatabinding

I real world scenario where I have used TaskCompletionSource is when implementing a download queue. In my case if the user starts 100 downloads I don't want to fire them all off at once and so instead of returning a strated task I return a task attached to TaskCompletionSource. Once the download gets completed the thread that is working the queue completes the task.
The key concept here is that I am decoupling when a client asks for a task to be started from when it actually gets started. In this case because I don't want the client to have to deal with resource management.
note that you can use async/await in .net 4 as long as you are using a C# 5 compiler (VS 2012+) see here for more details.

I've used TaskCompletionSource to run a Task until it is cancelled. In this case it's a ServiceBus subscriber that I normally want to run for as long as the application runs.
public async Task RunUntilCancellation(
CancellationToken cancellationToken,
Func<Task> onCancel)
{
var doneReceiving = new TaskCompletionSource<bool>();
cancellationToken.Register(
async () =>
{
await onCancel();
doneReceiving.SetResult(true); // Signal to quit message listener
});
await doneReceiving.Task.ConfigureAwait(false); // Listen until quit signal is received.
}

The Blazor's WebAssemblyHost also uses this to prevent .NET VM stop.
await new TaskCompletionSource().Task;

Related

Producer/consumer pattern in .NET with the ability to wait for submitted tasks/jobs to complete

I am trying to get my head around the following design, but fail to get a clear picture.
I have a number of producers submitting tasks/jobs to a queue. A consumer/worker would then pick these up and complete on these. For now, there is only one consumer/worker.
So far, this sounds like the standard producer/consumer pattern which could be done with a BlockingCollection.
However, some producers might want to submit a task/job and be able to wait for its completion (or submit multiple tasks/jobs and wait for some or all of them, etc.), while other producers would just "fire&forget" their tasks/jobs.
(Note that this is not waiting for the queue to be empty, but waiting for a particular task/job).
How would this be done? In all examples I have seen, producers just post data to the queue using BlockingQueue.Add().
Any help would be highly appreciated.
A common approach is to wrap your work operations using a TaskCompletionSource whose Task can be returned to the caller and awaited on for completion.
public class ProducerConsumerQueue
{
private readonly BlockingCollection<Action> queue = new BlockingCollection<Action>();
public Task Produce(Action work)
{
var tcs = new TaskCompletionSource<bool>();
Action action = () =>
{
try
{
work();
tcs.SetResult(true);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
};
queue.Add(action);
return tcs.Task;
}
public void RunConsumer(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
var action = queue.Take(token);
action();
}
}
}
That said, you should consider leveraging the task infrastructure provided by TPL itself, rather than coming up with your own structures. If your only requirement is having a bounded number of consumers, you could use a LimitedConcurrencyLevelTaskScheduler.

Immediately cancelling blocking operation with timeout

I have a blocking operation that reads from a queue, but it can take a timeout. I can easily convert this to an "async" operation:
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
return await Task.Run(() =>
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Try receiving for one second
IMessage message = consumer.Receive(TimeSpan.FromSeconds(1));
if (message != null)
{
return message;
}
}
}, cancellationToken).ConfigureAwait(false);
}
Aborting a thread is generally considered bad practice since you can leak resources, so the timeout seems like the only way to cleanly stop a thread. So I have three questions:
What is a generally accepted timeout value for "immediate" cancellation?
For libraries that provide built-in async methods, does immediate cancellation truly exist or do they also use timeouts and loops to simulate it? Maybe the question here is how would you make use of software interrupts and if these also have to do some sort of polling to check if there are interrupts, even if it's at the kernel/CPU level.
Is there some alternate way I should be approaching this?
Edit: So I may have found part of my answer with Thread.Interrupt() and then handling ThreadInterruptedException. Is this basically a kernel-level software interrupt and as close to "immediate" as we can get? Would the following be a better way of handling this?
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var completionSource = new TaskCompletionSource<IMessage>();
var receiverThread = new Thread(() =>
{
try
{
completionSource.SetResult(consumer.Receive());
}
catch (ThreadInterruptedException)
{
completionSource.SetCanceled();
}
catch (Exception ex)
{
completionSource.SetException(ex);
}
});
cancellationToken.Register(receiverThread.Interrupt);
receiverThread.Name = "Queue Receive";
receiverThread.Start();
return await completionSource.Task.ConfigureAwait(false);
}
It depends on your specific needs. A second could be immediate for some and slow for others.
Libraries (good ones) which provide async API do so from the bottom up. They usually don't wrap blocking (synchronous) operations with a thread to make them seem asynchronous. They use TaskCompletionSource to create truly async methods.
I'm not sure what you mean by queue (the built-in Queue in .Net doesn't have a Receive method) but you should probably be using a truly async data structure like TPL Dataflow's BufferBlock.
About your specific code sample.
You are holding up a thread throughout the entire operation (that's async over sync) which is costly. You could instead try to consume quickly and then wait asynchronously for the timeout to end, or for the CancellationToken to be cancelled.
There's also no point in using another thread with Task.Run. You can simply have the async lambda be the content of ReceiveAsync:
public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Try receiving for one second
IMessage message;
if (!consumer.TryReceive(out message))
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
if (message != null)
{
return message;
}
}
}
If your queue implements IDisposable a different (harsher) option would be to call Dispose on it when the CancellationToken is cancelled. Here's how.

How to convert this code to async await?

I have lots of code like this:
var feed = new DataFeed(host, port);
feed.OnConnected += (conn) =>
{
feed.BeginLogin(user, pass);
};
feed.OnReady += (f) =>
{
//Now I'm ready to do stuff.
};
feed.BeginConnect();
As you can see, I use the usual way of doing async operations. how do I change this code to use async await? Preferably something like this:
public async void InitConnection()
{
await feed.BeginConnect();
await feed.BeginLogin(user, pass);
//Now I'm ready
}
You can use TaskCompletionSource<T> to wrap your EAP (event-based async pattern) into Tasks. It's not clear how you handle errors and cancel operations in your DataFeed class, so you will need to modify this code and add error handling (sample):
private Task ConnectAsync(DataFeed feed)
{
var tcs = new TaskCompletionSource<object>();
feed.OnConnected += _ => tcs.TrySetResult(null);
feed.BeginConnect();
return tcs.Task;
}
private Task LoginAsync(DataFeed feed, string user, string password)
{
var tcs = new TaskCompletionSource<object>();
feed.OnReady += _ => tcs.TrySetResult(null);
feed.BeginLogin(user, pass);
return tcs.Task;
}
Now you can use these methods:
public async void InitConnection()
{
var feed = new DataFeed(host, port);
await ConnectAsync(feed);
await LoadAsync(feed, user, pass);
//Now I'm ready
}
Note - you can move these async methods to DataFeed class. But if you can modify DataFeed, then better use TaskFactory.FromAsync to wrap APM API to Tasks.
Unfortunately there is no non-generic TaskCompletionSource which would return non-generic Task so, usually workaround is usage of Task<object>.
You need to change your DataFeed class to support that. You'll just have to use the task asynchronous pattern in there. That means that all the asynchronous methods in DataFeed have to return a Task (or some Task<T>), and they should be named ConnectAsync (for example).
Now, with Socket, this isn't entirely easy, because the XXXAsync methods on Socket aren't actually awaitable! The easiest way is to simply use TcpClient and TcpListener respectivelly (provided you're using TCP):
public async Task<bool> LoginAsync(TcpClient client, ...)
{
var stream = client.GetStream();
await stream.WriteAsync(...);
// Make sure you read all that you need, and ideally no more. This is where
// TCP can get very tricky...
var bytesRead = await stream.ReadAsync(buf, ...);
return CheckTheLoginResponse(buf);
}
and then just use it from the outside:
await client.ConnectAsync(...);
if (!(await LoginAsync(client, ...))) throw new UnauthorizedException(...);
// We're logged in
This is just sample code, I assume you're actually able to write decent TCP code to start with. If you do, writing it asynchronously using await isn't really much harder. Just make sure you're always awaiting some asynchronous I/O operation.
If you want to do the same using just Socket, you will probably have to use Task.FromAsync, which provides a wrapper around the BeginXXX / EndXXX methods - very handy.

await asynchronous method where completion is signalled by event handler

I am working with the Contacts object in Windows Phone 8, calling SearchAysnc from within an async method. SearchAsync requires that a handler be subscribed to the SearchCompleted event, and delivers its results via one of the event args, which the async method requires to do its job (which includes invoking other async methods).
How do you await the asynchronous completion of an event i.e. bridge between the event pattern and the async/await pattern?
The only solution I could come up with was to use an EventWaitHandle, waiting on it within an awaited Task something like this:
using System.Threading;
async Task<string> MyMethod()
{
string result = null;
Contacts cons = new Contacts();
EventWaitHandle handle = new EventWaitHandle(false,EventResetMode.ManualReset);
cons.SearchCompleted += (sender,args) =>
{
// I do all my work on the results of the contact search in this handler
result = SomeOtherSynchronousOperation(args.Results);
// When I'm done, I release the thread which was waiting for the results
handle.Set();
};
cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
// I can't block this thread (or can I?)
// So, I launch a task whose sole job is to wait
await Task.Run(()=>
{
// This gets released by the Contacts.SearchCompleted handler when its work is finished,
// so that MyMethod can finish up and deliver its result
handle.WaitOne();
}
await DoOtherStuffWithResult(result);
return result;
}
My actual solution (not exactly as shown above) does work. Although the above code doesn't precisely represent the implemented solution, (likely a compile issue or two), it should serve to express the concept and illustrate the point of my question.
It leaves me wondering if this is the only way, or anywhere close to the best practise way to await the execution of an event handler, and if not, what would be the "best practice" to do what is needed here.
Do the Windows synchronization primitives still have a place in an async/await world?
(Based on answers provided)
Would this be correct?
using Microsoft.Phone.UserData;
string ExtractWhatIWantFromResults(IEnumerable<Contact> results)
{
string result;
// Do my processing on the list of contacts, stuff the results into result
return string;
}
async Task<string> MyMethod()
{
Contacts cons = new Contacts();
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
cons.SearchCompleted += (sender,args) =>
{
tcs.TrySetResult(ExtractWhatIWantFromResults(args.Results));
};
cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
return tcs.Task;
}
TaskCompletionSource is the common way to use.
Not tested (No idea how to test without knowing your classes/methods),
Task<string> MyMethodAsync()
{
Contacts cons = new Contacts();
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
cons.SearchCompleted += (sender,args) =>
{
tcs.TrySetResult(args.Results);
};
cons.SearchAsync(String.Empty, FilterKind.None, "My Contact");
return tcs.Task;
}
To bridge EAP and TAP, you should use TaskCompletionSource, as such:
public static Task<IEnumerable<Contact>> SearchTaskAsync(this Contacts contacts, string filter, FilterKind filterKind)
{
var tcs = new TaskCompletionSource<IEnumerable<Contact>>();
EventHandler<ContactsSearchEventArgs> subscription = null;
subscription = (_, e) =>
{
contacts.SearchCompleted -= subscription;
tcs.TrySetResult(e.Results);
};
contacts.SearchCompleted += subscription;
contacts.SearchAsync(filter, filterKind, null);
return tcs.Task;
}
which you can use like this:
async Task<string> MyMethodAsync()
{
Contacts cons = new Contacts();
var searchResults = await cons.SearchTaskAsync(String.Empty, FilterKind.None);
string result = SomeOtherSynchronousOperation(searchResults);
await DoOtherStuffWithResult(result);
return result;
}
In fact, the MSDN docs for TAP are really of an unusually high quality and I strongly recommend reading through that entire section.
Do the Windows synchronization primitives still have a place in an async/await world?
Not so much, because as soon as you block a thread, you lose the benefits of asynchronous code. That said, you can emulate similar behavior using TAP-based primitives; Stephen Toub has a series of blog entries that explore this and I implemented similar primitives in my AsyncEx library.

Aborting a long running task in TPL

Our application uses the TPL to serialize (potentially) long running units of work. The creation of work (tasks) is user-driven and may be cancelled at any time. In order to have a responsive user interface, if the current piece of work is no longer required we would like to abandon what we were doing, and immediately start a different task.
Tasks are queued up something like this:
private Task workQueue;
private void DoWorkAsync
(Action<WorkCompletedEventArgs> callback, CancellationToken token)
{
if (workQueue == null)
{
workQueue = Task.Factory.StartWork
(() => DoWork(callback, token), token);
}
else
{
workQueue.ContinueWork(t => DoWork(callback, token), token);
}
}
The DoWork method contains a long running call, so it is not as simple as constantly checking the status of token.IsCancellationRequested and bailing if/when a cancel is detected. The long running work will block the Task continuations until it finishes, even if the task is cancelled.
I have come up with two sample methods to work around this issue, but am not convinced that either are proper. I created simple console applications to demonstrate how they work.
The important point to note is that the continuation fires before the original task completes.
Attempt #1: An inner task
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() => Console.WriteLine("Token cancelled"));
// Initial work
var t = Task.Factory.StartNew(() =>
{
Console.WriteLine("Doing work");
// Wrap the long running work in a task, and then wait for it to complete
// or the token to be cancelled.
var innerT = Task.Factory.StartNew(() => Thread.Sleep(3000), token);
innerT.Wait(token);
token.ThrowIfCancellationRequested();
Console.WriteLine("Completed.");
}
, token);
// Second chunk of work which, in the real world, would be identical to the
// first chunk of work.
t.ContinueWith((lastTask) =>
{
Console.WriteLine("Continuation started");
});
// Give the user 3s to cancel the first batch of work
Console.ReadKey();
if (t.Status == TaskStatus.Running)
{
Console.WriteLine("Cancel requested");
cts.Cancel();
Console.ReadKey();
}
}
This works, but the "innerT" Task feels extremely kludgey to me. It also has the drawback of forcing me to refactor all parts of my code that queue up work in this manner, by necessitating the wrapping up of all long running calls in a new Task.
Attempt #2: TaskCompletionSource tinkering
static void Main(string[] args)
{ var tcs = new TaskCompletionSource<object>();
//Wire up the token's cancellation to trigger the TaskCompletionSource's cancellation
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token;
token.Register(() =>
{ Console.WriteLine("Token cancelled");
tcs.SetCanceled();
});
var innerT = Task.Factory.StartNew(() =>
{
Console.WriteLine("Doing work");
Thread.Sleep(3000);
Console.WriteLine("Completed.");
// When the work has complete, set the TaskCompletionSource so that the
// continuation will fire.
tcs.SetResult(null);
});
// Second chunk of work which, in the real world, would be identical to the
// first chunk of work.
// Note that we continue when the TaskCompletionSource's task finishes,
// not the above innerT task.
tcs.Task.ContinueWith((lastTask) =>
{
Console.WriteLine("Continuation started");
});
// Give the user 3s to cancel the first batch of work
Console.ReadKey();
if (innerT.Status == TaskStatus.Running)
{
Console.WriteLine("Cancel requested");
cts.Cancel();
Console.ReadKey();
}
}
Again this works, but now I have two problems:
a) It feels like I'm abusing TaskCompletionSource by never using it's result, and just setting null when I've finished my work.
b) In order to properly wire up continuations I need to keep a handle on the previous unit of work's unique TaskCompletionSource, and not the task that was created for it. This is technically possible, but again feels clunky and strange.
Where to go from here?
To reiterate, my question is: are either of these methods the "correct" way to tackle this problem, or is there a more correct/elegant solution that will allow me to prematurely abort a long running task and immediately starting a continuation? My preference is for a low-impact solution, but I'd be willing to undertake some huge refactoring if it's the right thing to do.
Alternately, is the TPL even the correct tool for the job, or am I missing a better task queuing mechanism. My target framework is .NET 4.0.
The real issue here is that the long-running call in DoWork is not cancellation-aware. If I understand correctly, what you're doing here is not really cancelling the long-running work, but merely allowing the continuation to execute and, when the work completes on the cancelled task, ignoring the result. For example, if you used the inner task pattern to call CrunchNumbers(), which takes several minutes, cancelling the outer task will allow continuation to occur, but CrunchNumbers() will continue to execute in the background until completion.
I don't think there's any real way around this other than making your long-running calls support cancellation. Often this isn't possible (they may be blocking API calls, with no API support for cancellation.) When this is the case, it's really a flaw in the API; you may check to see if there are alternate API calls that could be used to perform the operation in a way that can be cancelled. One hack approach to this is to capture a reference to the underlying Thread being used by the Task when the Task is started and then call Thread.Interrupt. This will wake up the thread from various sleep states and allow it to terminate, but in a potentially nasty way. Worst case, you can even call Thread.Abort, but that's even more problematic and not recommended.
Here is a stab at a delegate-based wrapper. It's untested, but I think it will do the trick; feel free to edit the answer if you make it work and have fixes/improvements.
public sealed class AbandonableTask
{
private readonly CancellationToken _token;
private readonly Action _beginWork;
private readonly Action _blockingWork;
private readonly Action<Task> _afterComplete;
private AbandonableTask(CancellationToken token,
Action beginWork,
Action blockingWork,
Action<Task> afterComplete)
{
if (blockingWork == null) throw new ArgumentNullException("blockingWork");
_token = token;
_beginWork = beginWork;
_blockingWork = blockingWork;
_afterComplete = afterComplete;
}
private void RunTask()
{
if (_beginWork != null)
_beginWork();
var innerTask = new Task(_blockingWork,
_token,
TaskCreationOptions.LongRunning);
innerTask.Start();
innerTask.Wait(_token);
if (innerTask.IsCompleted && _afterComplete != null)
{
_afterComplete(innerTask);
}
}
public static Task Start(CancellationToken token,
Action blockingWork,
Action beginWork = null,
Action<Task> afterComplete = null)
{
if (blockingWork == null) throw new ArgumentNullException("blockingWork");
var worker = new AbandonableTask(token, beginWork, blockingWork, afterComplete);
var outerTask = new Task(worker.RunTask, token);
outerTask.Start();
return outerTask;
}
}

Categories