You're currently calling the
new Task(Action<object>, object, CancellationToken)
constructor; the
TaskCreationOptions
you're passing in are ignored, because they're being treated as the
state
parameter to the
action
parameter.
You should generally try to avoid creating tasks which aren't already started. There are a few cases where it's necessary, but it's more efficient to use the
Task.Factory.StartNew
or
Task.Run
methods.
"Task.Factory.StartNew" vs "new Task(...).Start"[
^]
The
Parallel.Invoke
line doesn't look right to me. You're firing up two threads simply to start the tasks, which will run on a different thread. If you change your
CreateTask
method to return already-started tasks, you can simply remove this line.
You don't really need to call
Dispose
on the tasks, particularly in .NET 4.5:
Do I need to dispose of Tasks?[
^]
I'd be inclined to pass a single
CancellationTokenSource
to the
CreateTask
method, since you'll need to keep hold of it if you want to cancel the tasks.
If you're using .NET 4.5, I'd be inclined to use
async
/
await
, which makes TPL code much easier to write:
Asynchronous Programming with Async and Await (C# and Visual Basic)[
^]
Sacha Barber has a good series of articles on TPL:
Task Parallel Library: 1 of n[
^]
You might also want to check out Stephen Toub's blog posts:
Parallel Programming with .NET[
^]
Based on your expanded description, you probably want to break this down into smaller composable blocks.
The following code provides two task-returning methods:
WaitUntil
returns a cancellable Task
which will be completed on a specific date/time (to within 10 seconds);WhenFileChanged
returns a cancellable Task
which will be completed when a specified file is changed;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public static class TaskUtilities
{
private static Task FromCancellation()
{
var tcs = new TaskCompletionSource<bool>();
tcs.SetCanceled();
return tcs.Task;
}
public static Task WaitUntil(DateTimeOffset triggerTime, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested) return FromCancellation();
return new WaitUntilPromise(triggerTime, cancellationToken).Task;
}
private sealed class WaitUntilPromise
{
private const int CheckFrequencyInMilliseconds = 10000;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly DateTimeOffset _triggerTime;
private readonly CancellationToken _token;
private readonly CancellationTokenRegistration _registration;
private readonly Timer _timer;
public WaitUntilPromise(DateTimeOffset triggerTime, CancellationToken cancellationToken)
{
_triggerTime = triggerTime;
_token = cancellationToken;
if (triggerTime <= DateTimeOffset.UtcNow)
{
_taskCompletionSource.SetResult(true);
}
else
{
if (cancellationToken.CanBeCanceled)
{
_registration = cancellationToken.Register(
state => ((WaitUntilPromise)state).Complete(),
this);
}
_timer = new Timer(
state => ((WaitUntilPromise)state).CheckTime(),
this, 0, CheckFrequencyInMilliseconds);
}
}
public Task Task
{
get { return _taskCompletionSource.Task; }
}
private void CheckTime()
{
if (_triggerTime <= DateTimeOffset.UtcNow)
{
Complete();
}
}
private void Complete()
{
bool completed;
if (_token.IsCancellationRequested)
{
completed = _taskCompletionSource.TrySetCanceled();
}
else
{
completed = _taskCompletionSource.TrySetResult(true);
}
if (completed)
{
if (_timer != null) _timer.Dispose();
_registration.Dispose();
}
}
}
public static Task WhenFileChanged(string filePath, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(filePath))
{
throw new ArgumentNullException("filePath");
}
filePath = Path.GetFullPath(filePath);
if (!File.Exists(filePath))
{
throw new FileNotFoundException(null, filePath);
}
return new WhenFileChangedPromise(filePath, cancellationToken).Task;
}
private sealed class WhenFileChangedPromise
{
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly CancellationToken _token;
private readonly string _filePath;
private readonly CancellationTokenRegistration _registration;
private readonly FileSystemWatcher _watcher;
public WhenFileChangedPromise(string filePath, CancellationToken cancellationToken)
{
_filePath = filePath;
_token = cancellationToken;
string directoryName = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
_watcher = new FileSystemWatcher(directoryName, fileName);
_watcher.Changed += OnChanged;
if (cancellationToken.CanBeCanceled)
{
_registration = cancellationToken.Register(
state => ((WhenFileChangedPromise)state).Complete(),
this);
}
_watcher.EnableRaisingEvents = true;
}
public Task Task
{
get { return _taskCompletionSource.Task; }
}
private void OnChanged(object sender, FileSystemEventArgs e)
{
if (string.Equals(e.FullPath, _filePath, StringComparison.OrdinalIgnoreCase))
{
Complete();
}
}
private void Complete()
{
bool completed;
if (_token.IsCancellationRequested)
{
completed = _taskCompletionSource.TrySetCanceled();
}
else
{
completed = _taskCompletionSource.TrySetResult(true);
}
if (completed)
{
_watcher.Changed -= OnChanged;
_watcher.Dispose();
_registration.Dispose();
}
}
}
}
With .NET 4.5, your assembly's code would then look something like this:
public async Task Run()
{
while (shouldContinueToScheduleWork)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task configChanged = MonitorConfiguration(tokenSource.Token);
Task scheduleWork = ScheduleWork(tokenSource.Token);
Task completed = await Task.WhenAny(configChanged, scheduleWork);
tokenSource.Cancel();
if (completed == configChanged)
{
await ReloadConfiguration();
}
}
}
private Task MonitorConfiguration(CancellationToken cancellationToken)
{
return TaskUtilities.WhenFileChanged(pathToTheConfiguration, cancellationToken);
}
private async Task ReloadConfiguration()
{
...
}
private async Task ScheduleWork(CancellationToken cancellationToken)
{
await TaskUtilities.WaitUntil(timeWhenWorkShouldRun, cancellationToken);
if (!cancellationToken.IsCancellationRequested)
{
await DoWork(cancellationToken);
}
}
private async Task DoWork(CancellationToken cancellationToken)
{
...
}