4

次のようにレイアウトするコードがいくつかあります。

クラス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;
            }
        );

Class2 (タスクヘルパー)

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

TaskHelper は、いくつかのメソッドに含まれていた一連の冗長なタスク コードを標準化するために作成したクラスです。HttpWebRequest を受け取り、それを Task で実行し、ContinueWith ブロックで応答を取得し、応答を に解析しList<T>、 を返すためだけに存在しList<T>ます。

Class1 で続行する前に InvokeTaskMagic メソッドから結果を取得する必要があるため、Class1 の TaskHelper への呼び出しをタスクでラップしました (つまり、class1 での次のステップは、を使用することList<T>です。コードのコメントにあるように、私の問題はTaskHelper クラスの InvokeTaskMagic メソッドからの応答を待機していないため、class1 の Task は毎回空のリストを返します。

これは期待されていますか?TaskHelper.InvokeTaskMagic が戻るまで getSessionsTask を待機させる方法はありますか?

更新: 作業コードは次のとおりです。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>();
            }
        }); 
    }
}

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

2 に答える 2

8

これは期待されていますか?class1.task1continueblockをclass2.task2continueblockを待機させる方法はありますか?

ContinueWithもちろん、最初のタスクではなく、2番目のタスクの継続を呼び出してください。

両方が完了するまで待つ必要がある場合は、を使用できますTask.Factory.ContinueWhenAll

于 2012-12-19T15:30:41.717 に答える
1

しばらく前にあなたのコードに似たようなことをしましたが、エラー処理などで正しく動作させるのは難しいです。

最初に、異なるリターンタイプの継続を処理するためのジェネリックメソッドを作成しました

private AsyncCallback EndAsync<T1, T2>(TaskCompletionSource<T2> tcs,
                                       Func<IAsyncResult, T1> endMethod, 
                                       Func<T1, T2> continueWith) {
    return ar => {
        T1 result1;
        try {
            result1 = endMethod(ar);
        }
        catch (Exception err) {
            tcs.SetException(err);
            return;
        }

        try {
            T2 result2 = continueWith(result1);
            tcs.SetResult(result2);
        }
        catch (Exception err) {
            tcs.SetException(err);
            return;
        }
    };
}

次に、次のような非同期タスクを作成します。

public Task<List<T>> GetDataAsync(IProxy proxy) {
    var tcs = new TaskCompletionSource<List<T>>();
    var asyncCallback = EndAsync(tcs, 
                                 proxy.EndGetData, 
                                 result => result != null ? 
                                           ProcessResult(result) : 
                                           new List<T>());
    proxy.BeginGetData(asyncCallback);

    return tcs.Task;
}
于 2012-12-20T15:31:25.947 に答える