【发布时间】:2019-05-10 09:02:08
【问题描述】:
我需要在延迟后执行一种LongRunning 任务。
每个任务都可以取消。我更喜欢TPL 和cancellationToken。
由于我的任务运行时间很长,并且在开始任务之前必须将其放入字典中,因此我必须使用 new Task()。但是我遇到了不同的行为——当在Cancel() 之后使用new Task() 创建任务时,它会抛出TaskCanceledException,而使用Task.Run 创建的任务不会抛出异常。
通常我需要识别差异而不是TaskCanceledException。
这是我的代码:
internal sealed class Worker : IDisposable
{
private readonly IDictionary<Guid, (Task task, CancellationTokenSource cts)> _tasks =
new Dictionary<Guid, (Task task, CancellationTokenSource cts)>();
public void ExecuteAfter(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
var cts = new CancellationTokenSource();
var task = new Task(async () =>
{
await Task.Delay(waitBeforeExecute, cts.Token);
action();
}, cts.Token, TaskCreationOptions.LongRunning);
cancellationId = Guid.NewGuid();
_tasks.Add(cancellationId, (task, cts));
task.Start(TaskScheduler.Default);
}
public void ExecuteAfter2(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
var cts = new CancellationTokenSource();
cancellationId = Guid.NewGuid();
_tasks.Add(cancellationId, (Task.Run(async () =>
{
await Task.Delay(waitBeforeExecute, cts.Token);
action();
}, cts.Token), cts));
}
public void Abort(Guid cancellationId)
{
if (_tasks.TryGetValue(cancellationId, out var value))
{
value.cts.Cancel();
//value.task.Wait();
_tasks.Remove(cancellationId);
Dispose(value.cts);
Dispose(value.task);
}
}
public void Dispose()
{
if (_tasks.Count > 0)
{
foreach (var t in _tasks)
{
Dispose(t.Value.cts);
Dispose(t.Value.task);
}
_tasks.Clear();
}
}
private static void Dispose(IDisposable obj)
{
if (obj == null)
{
return;
}
try
{
obj.Dispose();
}
catch (Exception ex)
{
//Log.Exception(ex);
}
}
}
internal class Program
{
private static void Main(string[] args)
{
Action act = () => Console.WriteLine("......");
Console.WriteLine("Started");
using (var w = new Worker())
{
w.ExecuteAfter(act, TimeSpan.FromMilliseconds(10000), out var id);
//w.ExecuteAfter2(act, TimeSpan.FromMilliseconds(10000), out var id);
Thread.Sleep(3000);
w.Abort(id);
}
Console.WriteLine("Enter to exit");
Console.ReadKey();
}
}
UPD:
这种方法也无一例外地有效
public void ExecuteAfter3(Action action, TimeSpan waitBeforeExecute, out Guid cancellationId)
{
var cts = new CancellationTokenSource();
cancellationId = Guid.NewGuid();
_tasks.Add(cancellationId, (Task.Factory.StartNew(async () =>
{
await Task.Delay(waitBeforeExecute, cts.Token);
action();
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default), cts)); ;
}
【问题讨论】:
-
您可以对照枚举docs.microsoft.com/de-de/dotnet/api/…检查任务的
Status属性 -
它对我有什么帮助?
Task.Run不会抛出异常,而new Task()会。我需要弄清楚为什么会这样。 -
他们都抛出异常老兄
-
@JohnChris 这是因为
//value.task.Wait();我刚刚更新了我的代码 -
@isxaker 不要使用
new Task。任务不是线程,它们是承诺。没有理由使用冷任务。Task.Run了解取消,returns a task in the cancelled state 了解令牌是否已取消。
标签: c# .net task task-parallel-library cancellation