web-dev-qa-db-ja.com

C#でタスクが完了するまで待つにはどうすればよいですか?

サーバーにリクエストを送信し、返された値を処理したい:

_private static string Send(int id)
{
    Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
    string result = string.Empty;
    responseTask.ContinueWith(x => result = Print(x));
    responseTask.Wait(); // it doesn't wait for the completion of the response task
    return result;
}

private static string Print(Task<HttpResponseMessage> httpTask)
{
    Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
    string result = string.Empty;
    task.ContinueWith(t =>
    {
        Console.WriteLine("Result: " + t.Result);
        result = t.Result;
    });
    task.Wait();  // it does wait
    return result;
}
_

Taskを正しく使用していますか? Send()メソッドは毎回_string.Empty_を返し、Printは正しい値を返すため、そうは思いません。

私は何を間違えていますか?サーバーから正しい結果を取得するにはどうすればよいですか?

31
user266003

Printメソッドは、継続が完了するまで待機する必要があります(ContinueWithは、待機できるタスクを返します)。そうでない場合、2番目のReadAsStringAsyncが終了すると、メソッドは戻ります(結果が継続で割り当てられる前)。 sendメソッドにも同じ問題があります。どちらも、期待どおりの結果を一貫して得るために継続を待つ必要があります。以下に類似

private static string Send(int id)
{
    Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
    string result = string.Empty;
    Task continuation = responseTask.ContinueWith(x => result = Print(x));
    continuation.Wait();
    return result;
}

private static string Print(Task<HttpResponseMessage> httpTask)
{
    Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
    string result = string.Empty;
    Task continuation = task.ContinueWith(t =>
    {
        Console.WriteLine("Result: " + t.Result);
        result = t.Result;
    });
    continuation.Wait();  
    return result;
}
31
Kenneth Ito

client.GetAsync("aaaaa");を待機しますが、result = Print(x)を待機しません

responseTask.ContinueWith(x => result = Print(x)).Wait()を試してください

-編集-

Task responseTask = Task.Run(() => { 
    Thread.Sleep(1000); 
    Console.WriteLine("In task"); 
});
responseTask.ContinueWith(t=>Console.WriteLine("In ContinueWith"));
responseTask.Wait();
Console.WriteLine("End");

上記のコードは出力を保証しません:

In task
In ContinueWith
End

ただし、これは(newTaskを参照)

Task responseTask = Task.Run(() => { 
    Thread.Sleep(1000); 
    Console.WriteLine("In task"); 
});
Task newTask = responseTask.ContinueWith(t=>Console.WriteLine("In ContinueWith"));
newTask.Wait();
Console.WriteLine("End");
6
L.B

継続を使用する場合、.ContinueWithを記述する場所を、「内部」のステートメントではなく、直後のステートメントに実行が継続する場所として考えると便利です。その場合、Sendで空の文字列が返されることが明らかになります。応答の唯一の処理がコンソールへの書き込みである場合、Itoのソリューションで待機する必要はありません。コンソールの印刷は待機なしで行われますが、その場合は送信と印刷の両方がvoidを返す必要があります。これをコンソールアプリで実行すると、ページの印刷が得られます。

IMO、waits、およびTask.Resultの呼び出し(ブロック)は、目的の制御フローに応じて必要になる場合がありますが、多くの場合、実際には非同期機能を正しく使用していないことを示しています。

namespace TaskTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Send();
            Console.WriteLine("Press Enter to exit");
            Console.ReadLine();
        }

        private static void Send()
        {
            HttpClient client = new HttpClient();
            Task<HttpResponseMessage> responseTask = client.GetAsync("http://google.com");
            responseTask.ContinueWith(x => Print(x));
        }

        private static void Print(Task<HttpResponseMessage> httpTask)
        {
            Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
            Task continuation = task.ContinueWith(t =>
            {
                Console.WriteLine("Result: " + t.Result);
            });
        }
    }
}
0
Tony
async Task<int> AccessTheWebAsync()  
{   
    // You need to add a reference to System.Net.Http to declare client.  
    HttpClient client = new HttpClient();  

    // GetStringAsync returns a Task<string>. That means that when you await the  
    // task you'll get a string (urlContents).  
    Task<string> getStringTask = 

    client.GetStringAsync("http://msdn.Microsoft.com");  

    // You can do work here that doesn't rely on the string from GetStringAsync.  
    DoIndependentWork();  

    // The await operator suspends AccessTheWebAsync.  
    //  - AccessTheWebAsync can't continue until getStringTask is complete.  
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.  
    //  - Control resumes here when getStringTask is complete.   
    //  - The await operator then retrieves the string result from 
    getStringTask.  
    string urlContents = await getStringTask;  

    // The return statement specifies an integer result.  
    // Any methods that are awaiting AccessTheWebenter code hereAsync retrieve the length 
    value.  
    return urlContents.Length;  
}  
0
Nainesh Patel

私は非同期初心者なので、ここで何が起こっているのかを明確に伝えることはできません。メソッド内でタスクを内部で使用している場合でも、メソッド実行の期待値に不一致があると思われます。 Printを変更してTask <string>を返す場合、期待どおりの結果が得られると思います。

private static string Send(int id)
{
    Task<HttpResponseMessage> responseTask = client.GetAsync("aaaaa");
    Task<string> result;
    responseTask.ContinueWith(x => result = Print(x));
    result.Wait();
    responseTask.Wait(); // There's likely a better way to wait for both tasks without doing it in this awkward, consecutive way.
    return result.Result;
}

private static Task<string> Print(Task<HttpResponseMessage> httpTask)
{
    Task<string> task = httpTask.Result.Content.ReadAsStringAsync();
    string result = string.Empty;
    task.ContinueWith(t =>
    {
        Console.WriteLine("Result: " + t.Result);
        result = t.Result;
    });
    return task;
}
0
Jacob Proffitt

タイトルに答えるわかりやすい例

string output = "Error";
Task task = Task.Factory.StartNew(() =>
{
    System.Threading.Thread.Sleep(2000);
    output = "Complete";
});

task.Wait();
Console.WriteLine(output);
0
vikingfabian