I am trying to create a task tracker in C#. Each task has a unique id/name and a Task that would run "one instance per id can run at the same time.
I created the following class to track tasks
public partial class ProgressTracker : IProgressTracker
{
private ConcurrentDictionary<string, TrackedProgress> _tracker;
public ProgressTracker()
{
_tracker = new ConcurrentDictionary<string, TrackedProgress>();
}
public bool TryStart(string name, Task task)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
return _tracker.TryAdd(name, new TrackedProgress(task));
}
public bool TryFinish(string name, out int? duration)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
duration = null;
if (_tracker.TryRemove(name, out TrackedProgress progress))
{
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
public bool IsRunning(string name, out int? duration)
{
duration = null;
if (_tracker.TryGetValue(name, out TrackedProgress progress))
{
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
public bool TryAbort(string name, out int? duration)
{
duration = null;
if (_tracker.TryGetValue(name, out TrackedProgress progress))
{
if(!progress.Job.IsCanceled && !progress.Job.IsCompleted)
{
// How can I abort progress.Job task
}
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
}
Here is the class that contains the data about the tracked task.
public sealed class TrackedProgress
{
internal DateTime StartedAt { get; set; }
public Task Job { get; set; }
public int? Duration { get; set; }
public TrackedProgress(Task job)
{
StartedAt = DateTime.Now;
Job = job;
}
public TrackedProgress(DateTime startedAt, Task job)
{
StartedAt = startedAt;
Job = job;
}
}
Since I am tracking a Task
, how can I abort it upon request?
CodePudding user response:
You can add cts to the TrackedProgress class:
public sealed class TrackedProgress
{
internal DateTime StartedAt { get; set; }
public Task Job { get; set; }
public CancellationTokenSource Cts { get; }
public int? Duration { get; set; }
public TrackedProgress(Task job, CancellationTokenSource cts)
{
StartedAt = DateTime.Now;
Job = job;
Cts = cts;
}
public TrackedProgress(DateTime startedAt, Task job, CancellationTokenSource cts)
{
StartedAt = startedAt;
Job = job;
Cts = cts;
}
}
And few changes in ProgressTracker:
public partial class ProgressTracker
{
private ConcurrentDictionary<string, TrackedProgress> _tracker;
public ProgressTracker()
{
_tracker = new ConcurrentDictionary<string, TrackedProgress>();
}
//Here - add the cts
public bool TryStart(string name, Task task, CancellationTokenSource cts)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
return _tracker.TryAdd(name, new TrackedProgress(task, cts));
}
public bool TryFinish(string name, out int? duration)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
duration = null;
if (_tracker.TryRemove(name, out TrackedProgress progress))
{
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
public bool IsRunning(string name, out int? duration)
{
duration = null;
if (_tracker.TryGetValue(name, out TrackedProgress progress))
{
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
public bool TryAbort(string name, out int? duration)
{
duration = null;
if (_tracker.TryGetValue(name, out TrackedProgress progress))
{
if(!progress.Job.IsCanceled && !progress.Job.IsCompleted)
{
// Cancelling the task
progress.Cts.Cancel();
}
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
}
Of course you need to make sure you handle cancellation inside the task body too!
CodePudding user response:
After reading the comments, I tweaked my consumer class which import data which may take time. This class will accept a task and a cancellation token. The cancellation token is tracked along with the Task
The tracker class class now looks like this
internal sealed class TrackedProgress
{
public DateTime StartedAt { get; private set; }
public Task Job { get; private set; }
public CancellationTokenSource CancellationToken { get; private set; }
public int? Duration { get; private set; }
public TrackedProgress(Task job, CancellationToken cancellationToken)
: this(DateTime.Now, job, cancellationToken)
{
}
public TrackedProgress(DateTime startedAt, Task job, CancellationToken cancellationToken)
{
StartedAt = startedAt;
Job = job;
CancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
}
public void Abort()
{
CancellationToken?.Cancel();
}
public bool IsRunning()
{
if (Job == null)
{
return false;
}
return !Job.IsCompleted && !Job.IsCanceled;
}
}
The tracker class was modified to this
public partial class ProgressTracker : IProgressTracker
{
private ConcurrentDictionary<string, TrackedProgress> _tracker;
public ProgressTracker()
{
_tracker = new ConcurrentDictionary<string, TrackedProgress>();
}
public bool TryStart(string name, Task task, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
return _tracker.TryAdd(name, new TrackedProgress(task, cancellationToken));
}
public bool TryFinish(string name, out int? duration)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
duration = null;
if (_tracker.TryRemove(name, out TrackedProgress progress))
{
if(progress.IsRunning())
{
return false;
}
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
public bool IsTracked(string name, out int? duration)
{
duration = null;
if (_tracker.TryGetValue(name, out TrackedProgress progress))
{
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
public bool TryAbort(string name, out int? duration)
{
duration = null;
if (_tracker.TryGetValue(name, out TrackedProgress progress))
{
if(progress.IsRunning())
{
progress.Abort();
}
duration = (int)DateTime.Now.Subtract(progress.StartedAt).TotalSeconds;
return true;
}
return false;
}
}