管理Task.Run并停止/取消方法并取得进展

时间:2016-05-25 12:50:03

标签: c# task

我有一个当前按时间间隔运行任务的应用程序,但是我希望能够更好地控制它,以便能够停止正在运行的任务并通过单击UI重新启动它。

目前有6项任务,但我希望保持通用性,以便在需要时能够轻松完成。我希望能够创建一个包装器来控制它们,我可以将方法作为参数传递给它。

因此我创建了一个对象,我创建了与任务一样多的对象,我可以从中获取状态更新以及管理它

我想:   - 启动方法/任务   - 停止方法/任务   - 重新启动方法/任务   - 从我记录到更新列表

的日志/更新/进度/错误中获取反馈

这是一个很好的方法吗,有没有更好的方法来实现我的目标?

public class ManagedTask
    {
        public ManagedTask()
        {
            CreateNewToken();
        }

        public int Id { get; set; }
        public string DescriptiveName { get; set; }
        public Action<CancellationToken> TheVoidToRun { private get; set; }
        private CancellationTokenSource CTokenSource { get; set; }
        private CancellationToken CToken { get; set; }
        private Task TheRunningThing { get; set; }

        public void StartIt()
        {
            if (TheRunningThing == null || TheTaskStatus() == TaskStatus.Canceled || TheTaskStatus() == TaskStatus.RanToCompletion)
            {
                CreateNewToken();
            }
            // Start up the Task
            AddUpdate($"Starting Task at {DateTime.Now}");
            TheRunningThing = Task.Run(() => TheVoidToRun?.Invoke(CToken), CToken);
            AddUpdate($"Started Task at {DateTime.Now}");
        }
        public void EndIt()
        {
            AddUpdate($"Cancelling Task at {DateTime.Now}");
            CTokenSource.Cancel();
            // Do - If in progress try to stop (Cancellation Token)
            // Do - Stop future repeats
        }

        private void CreateNewToken()
        {
            CTokenSource = new CancellationTokenSource();
            CTokenSource.Token.ThrowIfCancellationRequested();
            CToken = CTokenSource.Token;
        }

        public TaskStatus TheTaskStatus() => TheRunningThing.Status;


        internal List<string> Updates { get; set; }

        private void AddUpdate(string updates)
        {
            // Do stuff
        }
    }

所以我有各种各样的方法,我想将其传递给这样的方式:

public class AvailableTasks
{
    public async void DoStuffThatIsCancelable(CancellationToken token)
    {
        DoTheLongStuffOnRepeat(token);
    }
    public async void DoAnotherThingThatIsCancelable(CancellationToken token)
    {
        DoTheLongStuffOnRepeat(token);
    }

    private async void DoTheLongStuffOnRepeat(CancellationToken token)
    {
        // Do stuff
        for (int i = 0; i < 20; i++)
        {
            while (!token.IsCancellationRequested)
            {
                try
                {
                    await Task.Delay(500, token);
                }
                catch (TaskCanceledException ex)
                {
                    Console.WriteLine("Task was cancelled");
                    continue;
                }
                Console.WriteLine($"Task Loop at {(i + 1) * 500}");
            }
        }
    }
}

以下是我打算如何调用它。

    private static readonly List<ManagedTask> _managedTasks = new List<ManagedTask>();

    public static void SetupManagedTasks()
    {
        var at = new AvailableTasks();
        var mt1 = new ManagedTask
        {
            Id = 1,
            DescriptiveName = "The cancelable task",
            TheVoidToRun = at.DoStuffThatIsCancelable,
        };
        _managedTasks.Add(mt1);

        var mt2 = new ManagedTask
        {
            Id = 2,
            DescriptiveName = "Another cancelable task",
            TheVoidToRun = at.DoAnotherThingThatIsCancelable,
        };
        _managedTasks.Add(mt2);

        mt1.StartIt();
        mt2.StartIt();

        Console.WriteLine($"{mt1.DescriptiveName} status: {mt1.TheTaskStatus()}");
        Console.WriteLine($"{mt2.DescriptiveName} status: {mt2.TheTaskStatus()}");
    }

    public static void CancelTask(int id)
    {
        var mt = _managedTasks.FirstOrDefault(t => t.Id == id);

        if (mt != null)
        {
            mt.EndIt();
            Console.WriteLine($"{mt.DescriptiveName} status: {mt.TheTaskStatus()}");
        }
    }

    public static void GetTaskStatus(int id)
    {
        var mt = _managedTasks.FirstOrDefault(t => t.Id == id);

        if (mt != null)
        {
            Console.WriteLine($"{mt.DescriptiveName} status: {mt.TheTaskStatus()}");
        }
    }

然而,即使有了上述所有内容,我仍然只能使用状态来显示RanToCompletion。

如何构建上述内容以实现我想要的目标?

谢谢, 大卫

1 个答案:

答案 0 :(得分:0)

  

我的状态只会出现RanToCompletion。

这是因为您的方法正在使用async void。它们应该是async Task。正如我在async best practices article中所描述的那样,您应该避免使用async void

其他说明......

  

启动方法/任务

     

重新启动方法/任务

您可以使用Task.Run在线程池上启动(或重新启动)任务。但是,如果您有自然的异步任务,那么您可以将它们表示为Func<Task>,只需调用Func<Task>即可启动它们。

  

停止方法/任务

执行此操作的唯一合适方法是使用CancellationToken,看起来您正常使用。

  

从我记录到更新列表

的日志/更新/进度/错误中获取反馈

我建议您使用IProgress<T>进行任何类型的进度更新。