取消长字符串"任务延续正确吗?强制执行任务执行顺序和线程使用?

时间:2016-02-11 18:17:54

标签: c# task-parallel-library

我有一个类将小块IO工作串起来作为Task个续点。每次收到一件作品时,都会创建一个新的Task,然后将其作为LastCreatedTask的续篇添加。我正在尝试确定正确取消所有这些Task的正确方法吗?

这是我目前的设置

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;
}

所以在函数ScheduleChunk a" chunk"将IO工作(和超时)传递给CreateProcessTask,这将创建一个Task,用于执行IO工作的实际处理。 CancellationToken传递给此Task,这是通过将两个CancellationTokenSources链接在一起而制作的。

第一个是"主要" CancellationTokenSource;我希望能够简单地在此来源上调用Cancel来取消链接的所有Task。第二个源是在一段给定的时间后自动取消的源(这将停止长时间运行/停止的IO块)。

最后,一旦构造的Task返回到ScheduleChunk,它就会被添加为LastCreatedTask的延续,这是作为延续添加的最后一个任务。这实际上使Task链一个接一个地运行。

1。 我的方法是否取消Task链的正确方法?致电Cancel上的TaskQueueTokenSource

2。 是否正确使用TaskContinuationOptions.ExecuteSynchronously和continuation以确保这些Task按顺序依次执行?< / em>

3。 有没有办法指定我希望基础TPL ThreadPool中的同一个线程能够在Task链上工作?< / em>

据我所知,不应该为每个延续点创建一个新线程,尽管在某些时候新线程可以拾取链。

2 个答案:

答案 0 :(得分:2)

现有代码根本不起作用:

LastCreatedTask.ContinueWith((t) => ioChunkProcessTask)

此延续只返回一个任务,其结果几乎立即被设置为一个常量对象。这就是全部。

实际上,这段代码的结构很笨拙。这要好得多:

async Task RunProcessing() {
 while (...) {
  await CreateProcessTask(...);
 }
}

await用于对异步操作进行排序。 <{1}}可以在大多数时间内取代await

取消看起来不错。也许它可以简化一点,但它工作正常。

关于3,你永远不需要这个。线程亲和力是一种罕见的事情,需要避免。没有办法完全按照要求这样做。请详细说明你想要达到的目标。

如果你坚持使用Task.Run,​​这里有一个草图:

ContinueWith

答案 1 :(得分:0)

使用您传递给每个任务的共享取消令牌,而不是为每个任务创建唯一的令牌。取消该令牌后,使用该令牌的所有任务将知道停止处理。

我回答后编辑了,所以我会回复您的个别编号问题:

1。 我的方法是否正确取消任务链?通过在TaskQueueTokenSource上调用Cancel?

According to msdn,最佳做法是创建一个令牌,然后将相同的令牌传递给每个任务。

2. 是否正确使用TaskContinuationOptions.ExecuteSynchron和continuation正确的方法来确保这些Task一个接一个地执行?

不,你的任务是要并行运行的,最好的做法是让依赖于命令的任务在链中相互调用,第一个调用第二个,依此类推。或者,您可以等到第一个任务完成后再开始第二个任务。

3. 有没有办法指定我希望底层TPL ThreadPool中的同一个线程能够在这个Task链上工作?

你不太可能这样做,这是线程池和任务异步编程(TAP)的目的的一部分,而TPL是抽象显式线程。您无法保证运行任务的线程,甚至无法在没有大量工作的情况下为该任务生成新线程。

那就是说,如果由于某种原因你确实需要这样做a custom task scheduler is the answer