Home > Blockchain >  How to abort tacked tasks in C#?
How to abort tacked tasks in C#?

Time:10-18

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;
        }
    }
  • Related