来自2级Task.ContinueWith的意外行为

And*_*ltz 4 c# task-parallel-library

我有一些代码如下:

1类

Task<List<ConfSession>> getSessionsTask = Task.Factory.StartNew(() =>
            {
                var confSessions = 
                    TaskHelper<ConfSession>.InvokeTaskMagic(request);
                //PROBLEM - THIS CODE RACES TO THE NEXT LINE 
                //BEFORE confSessions IS POPULATED FROM THE LINE ABOVE - IE 
                //confSessions IS ALWAYS AN EMPTY LIST 
                //WHEN IT'S RETURNED
                return confSessions;
            }
        );
Run Code Online (Sandbox Code Playgroud)

Class2(TaskHelper)

//methods
public static List<T> InvokeTaskMagic(HttpWebRequest request)
{
    var resultList = new List<T>();

    Task<WebResponse> task1 = Task<WebResponse>.Factory.FromAsync(
        (callback, o) => ((HttpWebRequest)o).BeginGetResponse(callback, o)
        , result => ((HttpWebRequest)result.AsyncState).EndGetResponse(result)
        , request);

    task1.ContinueWith((antecedent) =>
    {
        if (antecedent.IsFaulted)
        {
            return;
        }

        WebResponse webResponse;
        try
        {
            webResponse = task1.Result;
        }
        catch (AggregateException ex1)
        {
            throw ex1.InnerException;
        }

        string responseString;

        using (var response = (HttpWebResponse)webResponse)
        {
            using (Stream streamResponse = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(streamResponse);
                responseString = reader.ReadToEnd();
                reader.Close();
            }
        }

        if (responseString != null)
        {
            resultList = 
               JsonConvert.DeserializeObject<List<T>>(responseString);
        }
    });

    return resultList;
}
Run Code Online (Sandbox Code Playgroud)

TaskHelper是我创建的一个类,用于标准化我在几种方法中使用的一堆冗余任务代码.它的存在只是为了获取HttpWebRequest,在Task中执行它,在ContinueWith块中获取响应,将响应解析为a List<T>,然后返回List<T>.

我在一个任务中包装了Class1对TaskHelper的调用,因为我需要在继续使用class1之前从InvokeTaskMagic方法获取结果(即class1中的下一步是使用List<T>.如在代码中的注释中所述,我的问题是class1中的Task每次返回一个空列表,因为它不等待TaskHelper类中的InvokeTaskMagic方法的响应.

这是预期的吗?有没有办法使getSessionsTask等待返回,直到TaskHelper.InvokeTaskMagic返回?

更新:工作代码如下 - 感谢Servy的帮助.

public static class TaskHelper<T> where T : class
{
    //methods
    public static Task<List<T>> InvokeTaskMagic(HttpWebRequest request)
    {
        var resultList = new List<T>();

        Task<WebResponse> task1 = Task<WebResponse>.Factory.FromAsync(
            (callback, o) => ((HttpWebRequest)o).BeginGetResponse(callback, o)
            , result => ((HttpWebRequest)result.AsyncState).EndGetResponse(result)
            , request);

        return task1.ContinueWith<List<T>>((antecedent) =>
        {
            if (antecedent.IsFaulted)
            {
                return new List<T>();
            } 

            WebResponse webResponse;
            try
            {
                webResponse = task1.Result;
            }
            catch (AggregateException ex1)
            {
                throw ex1.InnerException;
            }

            string responseString;

            using (var response = (HttpWebResponse)webResponse)
            {
                using (Stream streamResponse = response.GetResponseStream())
                {
                    StreamReader reader = new StreamReader(streamResponse);
                    responseString = reader.ReadToEnd();
                    reader.Close();
                }
            }

            if (responseString != null)
            {
                return JsonConvert.DeserializeObject<List<T>>(responseString);
            }
            else
            {
                return new List<T>();
            }
        }); 
    }
}
Run Code Online (Sandbox Code Playgroud)

InvokeTaskMagic方法的调用方式如下:

var getCommentsAboutTask = Task.Factory.StartNew(() =>
            {
                var comments = TaskHelper<Comment>.InvokeTaskMagic(request);
                return comments;
            });

        getCommentsAboutTask.ContinueWith((antecedent) =>
            {
                if (antecedent.IsFaulted)
                { return; }

                var commentList = antecedent.Result.Result;
                UIThread.Invoke(() =>
                {
                    foreach (Comment c in commentList)
                    {
                        AllComments.Add(c);
                        GetCommentRelatedInfo(c);
                    }
                });
            });
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 8

这是预期的吗?有没有办法让我的class1.task1 continueblock等待我的class2.task2 continueblock?

当然,只需要调用ContinueWith第二个任务的延续而不是第一个任务.

如果你需要它等到两者都完成后你可以使用Task.Factory.ContinueWhenAll.