我对使用的取消令牌源有疑问,如下面的代码所示:
void Process()
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
var cancellationToken = _cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
MainTaskRoutine();
}, cancellationToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
void MainTaskRoutine()
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
//this method shows that a nested task is created
var cancellationToken = _cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
}, cancellationToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
<小时/> 修改:进一步详细说明
最终目标是:当用户取消操作时,所有即时待处理任务(儿童或大孩子)都应取消。
情境: 按照上面的代码: 1.我首先检查用户是否要求取消 2.如果用户没有要求取消,则只继续执行任务(请参阅处理方法)。 示例代码在这里只显示一个任务,但实际上可以有三个或更多
让我们说CPU开始处理Task1,而其他任务仍然在Task队列中等待一些CPU来执行它们。 用户请求取消:Process方法中的任务2,3立即被取消,但任务1将继续工作,因为它已经在进行处理。
在任务1中,它调用方法MainTaskRoutine,后者又创建了更多任务。
在MainTaskRoutine的函数中我写了:cancellationToken.ThrowIfCancellationRequested();
所以问题是:是否正确使用CancellationTokenSource,因为它依赖于Task.WaitAll()?
答案 0 :(得分:3)
[已编辑] 当您在代码中使用数组时,我假设可能有多个任务,而不仅仅是一个。我还假设您从Process
开始的每个任务中都要首先执行一些CPU绑定工作(//do some work here
),然后运行MainTaskRoutine
。
如何处理任务取消例外由您的项目设计工作流程决定。例如,您可以在Process
方法内或从您调用Process
的位置执行此操作。如果您唯一关心的是从跟踪待处理任务的阵列中删除Task对象,可以使用Task.ContinueWith来完成。无论任务的完成状态如何(Cancelled
,Faulted
或RanToCompletion
),都会执行继续:
Task Process(CancellationToken cancellationToken)
{
var tArray = new List<Task>();
var tArrayLock = new Object();
var task = Task.Run(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
return MainTaskRoutine(cancellationToken);
}, cancellationToken);
// add the task to the array,
// use lock as we may remove tasks from this array on a different thread
lock (tArrayLock)
tArray.Add(task);
task.ContinueWith((antecedentTask) =>
{
if (antecedentTask.IsCanceled || antecedentTask.IsFaulted)
{
// handle cancellation or exception inside the task
// ...
}
// remove task from the array,
// could be on a different thread from the Process's thread, use lock
lock (tArrayLock)
tArray.Remove(antecedentTask);
}, TaskContinuationOptions.ExecuteSynchronously);
// add more tasks like the above
// ...
// Return aggregated task
Task[] allTasks = null;
lock (tArrayLock)
allTasks = tArray.ToArray();
return Task.WhenAll(allTasks);
}
您的MainTaskRoutine
可以与Process
完全相同的方式构建,并具有相同的方法签名(返回Task
)。
然后,您可能希望对Process
返回的聚合任务执行阻塞等待,或者异步处理其完成,例如:
// handle the completion asynchronously with a blocking wait
void RunProcessSync()
{
try
{
Process(_cancellationTokenSource.Token).Wait();
MessageBox.Show("Process complete");
}
catch (Exception e)
{
MessageBox.Show("Process cancelled (or faulted): " + e.Message);
}
}
// handle the completion asynchronously using ContinueWith
Task RunProcessAync()
{
return Process(_cancellationTokenSource.Token).ContinueWith((task) =>
{
// check task.Status here
MessageBox.Show("Process complete (or cancelled, or faulted)");
}, TaskScheduler.FromCurrentSynchronizationContext());
}
// handle the completion asynchronously with async/await
async Task RunProcessAync()
{
try
{
await Process(_cancellationTokenSource.Token);
MessageBox.Show("Process complete");
}
catch (Exception e)
{
MessageBox.Show("Process cancelled (or faulted): " + e.Message);
}
}
答案 1 :(得分:1)
在做了一些研究之后,我找到了this link。
代码现在看起来像这样: 在下面的代码中查看CancellationTokenSource.CreateLinkedTokenSource的用法
void Process()
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
var cancellationToken = _cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancellationToken.ThrowIfCancellationRequested();
//do some work here
MainTaskRoutine(cancellationToken);
}, cancellationToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
void MainTaskRoutine(CancellationToken cancellationToken)
{
//for the sake of simplicity I am taking 1, in original implementation it is more than 1
//this method shows that a nested task is created
using (var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
var cancelToken = cancellationTokenSource.Token;
Task[] tArray = new Task[1];
tArray[0] = Task.Factory.StartNew(() =>
{
cancelToken.ThrowIfCancellationRequested();
//do some work here
}, cancelToken);
try
{
Task.WaitAll(tArray);
}
catch (Exception ex)
{
//do error handling here
}
}
}
注意:我没有使用它,但一旦完成,我会告诉你:)