C# 取消一个“长”;“字符串”;任务延续的正确性?强制任务执行顺序和线程使用情况?
我有一个类,它将一小段IO工作串在一起,作为C# 取消一个“长”;“字符串”;任务延续的正确性?强制任务执行顺序和线程使用情况?,c#,task-parallel-library,C#,Task Parallel Library,我有一个类,它将一小段IO工作串在一起,作为任务的延续。每次收到一件作品时,都会创建一个新的任务,然后将其作为LastCreatedTask的延续添加到该任务中。我正在尝试确定正确取消所有这些任务的方法 这是我目前的设置 private Task LastCreatedTask { get; set; } private CancellationTokenSource TaskQueueTokenSource { get; set; } public void ScheduleChunk(IO
任务
的延续。每次收到一件作品时,都会创建一个新的任务
,然后将其作为LastCreatedTask
的延续添加到该任务中。我正在尝试确定正确取消所有这些任务的方法
这是我目前的设置
private Task LastCreatedTask { get; set; }
private CancellationTokenSource TaskQueueTokenSource { get; set; }
public void ScheduleChunk(IOWorkChunk inChunk, int procTimeout)
{
Task ioChunkProcessTask = CreateProcessTask(inChunk, procTimeout);
LastCreatedTask.ContinueWith(
(t) => ioChunkProcessTask.Start(),
TaskContinuationOptions.ExecuteSynchronously);
LastCreatedTask = ioChunkProcessTask;
}
private Task CreateProcessTask(IOWorkChunk inChunk, int procTimeout)
{
// Create a TokenSource that will cancel after a given timeout period
var ProcessTimeoutTokenSource = new CancellationTokenSource(
TimeSpan.FromSeconds(procTimeout));
// Create a TokenSource for the Task that is a
// link between the timeout and "main" token source
var ProcessTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
TaskQueueTokenSource.Token,
ProcessTimeoutTokenSource.Token);
// Create a Task that will do the actual processing
Task ioChunkProcessTask = new Task(() =>
{
if(!ProcessTokenSource.Token.IsCancellationRequested)
inChunk.DoProcessing(ProcessTokenSource.Token);
}, ProcessTokenSource.Token);
return ioChunkProcessTask;
}
因此,在函数schedulehunk
中,IO工作的“块”(以及超时)被传递给CreateProcessTask
,后者创建了一个Task
,该任务将执行IO工作的实际处理。一个CancellationToken
被传递给该任务,该任务是通过将两个CancellationTokenSources
链接在一起完成的
第一个是“main”CancellationTokenSource
;我希望能够简单地调用此源上的Cancel
,以取消链接的所有任务。第二个源是在给定时间段后自动取消的源(这将停止长时间运行/暂停的IO块)
最后,一旦构造的任务
返回到ScheduleChunk
中,它将作为延续添加到LastCreatedTask
中,后者是最后一个作为延续添加的任务。这实际上使一系列的任务
依次运行
1.我上面的方法是取消任务链的正确方法吗?通过调用TaskQueueTokenSource
上的Cancel
2.是否使用了TaskContinuationOptions。与continuations同步执行是确保这些Task
依次执行的正确方法
3.是否有办法指定我希望来自底层TPLThreadPool
的同一线程处理此任务链
据我所知,不应该为每个延续点创建一个新线程,尽管在某个点上,一个新线程可能会拾取链。使用传递给每个任务的共享取消令牌,而不是为每个任务创建一个唯一的取消令牌。当您取消该令牌时,使用该令牌的所有任务都将知道停止处理
您在我回答后进行了编辑,因此我将回答您的编号问题:
1.我的上述方法是否是取消任务链的正确方法?在TaskQueueTokenSource上调用Cancel
,最佳实践是创建一个令牌,然后将该令牌传递给每个任务
2.使用TaskContinuationOptions.Executes与continuations同步是否是确保这些任务按顺序依次执行的正确方法
不,您的任务应该是并行运行的,而最佳实践是让依赖顺序的任务在一个链上相互调用,第一个调用第二个,依此类推。或者,您可以等到第一个任务完成后再开始第二个任务
3.是否有办法指定我希望底层TPL线程池中的同一线程处理此任务链
您不太可能这样做,线程池和任务异步编程(TAP)以及TPL的部分目的是将显式线程抽象出来。在没有大量工作的情况下,无法保证运行任务的线程,甚至无法保证是否为该任务生成新线程
也就是说,如果出于某种原因确实需要执行此操作,那么现有代码根本不起作用:
LastCreatedTask.ContinueWith((t) => ioChunkProcessTask)
此继续操作仅返回一个任务,其结果将几乎立即设置为常量对象。这就是它的全部功能
实际上,这段代码的结构很笨拙。这样更好:
async Task RunProcessing() {
while (...) {
await CreateProcessTask(...);
}
}
wait
用于对异步操作进行排序<代码>等待
在大多数情况下都可以替换继续
取消看起来不错。也许它可以简化一点,但它工作得很好
关于3,你永远不应该需要这个。线程亲和力是一种罕见的东西,必须避免。没有办法完全按照要求做到这一点。请详细说明你想要实现的目标
如果您坚持使用Task.Run,下面是一个示意图:
Task CreateProcessTask(IOWorkChunk inChunk, int procTimeout)
{
// Create a TokenSource that will cancel after a given timeout period
var ProcessTimeoutTokenSource = new CancellationTokenSource(
TimeSpan.FromSeconds(procTimeout));
// Create a TokenSource for the Task that is a
// link between the timeout and "main" token source
var ProcessTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
TaskQueueTokenSource.Token,
ProcessTimeoutTokenSource.Token);
return Task.Run(() => {
inChunk.DoProcessing(ProcessTokenSource.Token);
}, ProcessTokenSource.Token);
}
即使您在取消令牌上调用了Cancel
,您仍然需要检查令牌是否已在所有任务中被取消。哦,是的,忘了在其中添加它。我马上就要编辑了。你能用wait吗?通常,所有任务链都应表示为异步方法。这使所有这些都变得容易。承认我缺乏知识,我不确定在这里使用async/await如何使这更容易?我对async/await有一定的理解,只是不理解它将如何应用。因此,我要回答的是,不,我不知道我是否可以或者我将如何做?我想这就是我使用TaskQueueTokenSource
所做的。这与CreateProcessTask
中的ProcessTimeoutTokenSource
相结合,创建一个CancellationTokenSource.Token
,传递给任务
?我正在回答您在提交答案时提出的问题,但是根据MSDN,您只向所有任务传递一个令牌。我为您的三个明确问题添加了答案。链接到1的MSDN文章没有明确说明向每个任务传递相同令牌是最佳实践,这正是示例中的做法。另外,我“基本上”在做这个,不是吗?至于2的答案,这就是我目前正在做的。我将我的任务与ContinueWith
链接起来。据我所知,这有一种“把对方叫下一个链子”的效果