Lambda任务中的捕获变量未更新

时间:2017-05-03 10:16:23

标签: c# task-parallel-library

我有一个标题列表,我试图通过使用更新标题对象参数的任务来处理。我尝试使用的代码实际上并没有正确填充参数。当我调试它时,我可以看到正在激活setter并正确更新支持字段,但是在Task.WhenAll之后检查时,实际上没有任何属性设置为它们的预期值。

//**Relevant signatures are:**
    class Headline{
        public Uri Uri { get; set; }
        public IEnumerable<string> AttachedEmails { get; set; } = Enumerable.Empty<string>();
    }


    async Task<IEnumerable<string>> GetEmailsFromHeadline(Uri headlineUri) {
        //bunch of async fetching logic that populates emailNodes correctly


        return emailNodes.Select(e => e.InnerText).ToList();
    }



//**Problem Area**

//Initiate tasks that start fetching information
        var taskList = 
            postData
                .Select(e => new HttpRequest() { Uri = actionUri, PostData = e })
                .Select(e => Task.Run(() => GetHeadlines(e)))
                .ToList();

//Wait till complete
        await Task.WhenAll(taskList);

//Flatten list 
        var allHeadlines = 
            taskList
                .SelectMany(e => e.Result.ToList());

//After this section of code I expect every member AttachedEmails property to be properly updated but this is not what is happening. 

            var headlineProcessTaskList =
                allHeadlines
                    .Select(e => Task.Run( new Func<Task>( async () => e.AttachedEmails = await GetEmailsFromHeadline(e.Uri) ) ) )
                    .ToList();

             await Task.WhenAll(headlineProcessTaskList);

1 个答案:

答案 0 :(得分:2)

没有足够的代码来重现问题。但是,我们可以猜到:

allHeadlines is IEnumerable<Headline>

由于这是IEnumerable<T>可能它是一个已被延迟的LINQ查询。因此,当您在ToList调用中枚举一次时,会创建设置了Headline的{​​{1}}个实例。但是当您稍后再次枚举 时,它会创建新的AttachedEmails个实例。

如果这是正确的,那么一种解决方案是将Headline的类型更改为allHeadlines

类似的问题可能发生在List<Headline>,可能会返回GetEmailsFromHeadline而不是Task<IEnumerable<string>>。如果枚举是延迟的,则唯一的异步部分是定义查询; 执行它将在事后 - 更具体地说,在IEnumerable<string>之外。您可能也想考虑在那里使用Task.Run

另一方面,ToList只是噪音,new Func中异步任务的包装非常不寻常。这将更加惯用:

Task.Run

或者,如果确实需要 var headlineProcessTaskList = allHeadlines .Select(async e => e.AttachedEmails = await GetEmailsFromHeadline(e.Uri) ) .ToList();

Task.Run