我有一个当前按时间间隔运行任务的应用程序,但是我希望能够更好地控制它,以便能够停止正在运行的任务并通过单击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。
如何构建上述内容以实现我想要的目标?
谢谢, 大卫
答案 0 :(得分:0)
我的状态只会出现RanToCompletion。
这是因为您的方法正在使用async void
。它们应该是async Task
。正如我在async
best practices article中所描述的那样,您应该避免使用async void
。
其他说明......
启动方法/任务
重新启动方法/任务
您可以使用Task.Run
在线程池上启动(或重新启动)任务。但是,如果您有自然的异步任务,那么您可以将它们表示为Func<Task>
,只需调用Func<Task>
即可启动它们。
停止方法/任务
执行此操作的唯一合适方法是使用CancellationToken
,看起来您正常使用。
从我记录到更新列表
的日志/更新/进度/错误中获取反馈
我建议您使用IProgress<T>
进行任何类型的进度更新。