访问Task.ContinueWith中的值

本文关键字:ContinueWith Task 访问 | 更新日期: 2023-09-27 18:14:57

我用下面的代码开始一个任务:

var token = tokenSource.Token;
var taskWithToken = new Task(() =>
        new ProcessMyCommand(_unitOfWork, ..., batchRunId, token).Execute(), 
        token);

在我的继续,我需要知道batchRunId和可能在...中列出的其他一些变量,但是,似乎这是不可能的??

taskWithToken.ContinueWith(task =>
        {
            if (!task.IsCanceled)
                return;
            //TODO: make sure no more subsequent runs happen
            //TODO: sync with source data
        }
    );

我错过了什么吗?我如何确保.ContinueWith在执行时能够访问它所需的值?

访问Task.ContinueWith中的值

首先,我甚至不确定您的情况是否需要继续。您的代码可以简化为如下内容:

var taskWithToken = new Task(() =>
    {
        new ProcessMyCommand(_unitOfWork, ..., batchRunId, token).Execute();
        // code from the continuation here
    },
    token);

但是,如果您确实想使用ContinueWith(),并且由于ReSharper警告而担心使用它,那么您不必这样做。大多数情况下,像这样的代码是完全没问题的,您可以忽略警告。

长版本:当你写一个lambda引用了封闭范围(所谓的闭包)的东西时,编译器必须为此生成代码。具体如何做是一个实现细节,但是当前的编译器为一个方法中的所有闭包生成一个闭包类。

在您的情况下,这意味着编译器生成一个包含局部变量this(因为_unitOfWork)、requestbatchRunId(可能还有您没有显示的其他变量)的类。这个闭包对象在new Task lambda和ContinueWith() lambda之间共享,即使第二个lambda不使用requestthis。只要第二个lambda从某处被引用,这些对象就不能被垃圾收集,即使它们不能被访问。

所以,这种情况可能导致内存泄漏,我相信这就是ReSharper警告你的原因。但在几乎所有情况下,这种内存泄漏要么不存在(因为第二个lambda的引用时间并不比第一个长),要么非常小。因此,大多数时候,您可以安全地忽略该警告。但是如果你发现了神秘的内存泄漏,你应该调查你使用lambdas的方式,特别是在你得到这个警告的地方。

您可以创建您的MyTaskData类来存储您的数据和结果,它也可以存储MyTaskData PreviousTaskData属性(从上一个任务)创建结果链表。创建一个Task<MyTaskData>,在里面,最后,你的return myNewTaskData;。然后是ContinueWith<MyTaskData>(...),您可以通过Task.Result属性获得之前的结果。至于在取消的Task上继续ContinueWith有一个带有TaskContinuationOptions参数(MSDN)的变体,您可以指定NotOnCanceled