9

昨日、Microsoft CTP 非同期ライブラリを使い始めましたが、待機可能なタスクの適切な実装がどこにも見つかりませんでした。私はそれがこのような実装を持っている必要があることを知っています?:

public struct SampleAwaiter<T>
{
    private readonly Task<T> task;
    public SampleAwaiter(Task<T> task) { this.task = task; }
    public bool IsCompleted { get { return task.IsCompleted; } }
    public void OnCompleted(Action continuation) { TaskEx.Run(continuation); }
    public T GetResult() { return task.Result; }
}

しかし、たとえば 5 秒間待機して、"Hello World" などの文字列を返すタスクを実装するにはどうすればよいでしょうか。

1 つの方法は、次のように Task を直接使用することです。

Task<string> task = TaskEx.Run(
            () =>
                {
                    Thread.Sleep(5000);
                    return "Hello World";
                });

        string str = await task;

しかし、待機可能な実装でそれを行うにはどうすればよいでしょうか? それとも、すべてを誤解しただけですか?

情報/ヘルプをありがとう:)

4

3 に答える 3

16

ここで重要なのはAsyncCtpThreadingExtensions.GetAwaiter、拡張メソッドを介してこれらのメソッドを提供することです。非同期実装はパターンベース (LINQ など) であるため、特定のインターフェイスに結び付けられるのではなく、どこからでも取得できます (TaskAwaiterこの場合)。

書かれたコードが待ち遠しいです。例えば:

static void Main()
{
    Test();
    Console.ReadLine(); // so the exe doesn't burninate
}
static async void Test() {
    Task<string> task = TaskEx.Run(
           () =>
           {
               Thread.Sleep(5000);
               return "Hello World";
           });
    string str = await task;
    Console.WriteLine(str);
}

Hello Worldこれは5 秒後に印刷されます。

于 2011-08-07T08:54:17.123 に答える
2

1年後に追加

async-await を 1 年以上使用した後、元の回答で書いた async に関するいくつかのことが正しくないことがわかりましたが、回答のコードはまだ正しいです。Hera は、async-await の仕組みを理解するのに大いに役立った 2 つのリンクです。

このインタビュー Eric Lippert は、 async-await の優れた例えを示しています。途中で async-await を検索します。

この記事では、非常に役立つ Eric Lippert が、async-await の優れたプラクティスを示しています。

元の答え

OK、これは学習プロセス中に私を助けた完全な例です。

遅い電卓があり、ボタンを押すときにそれを使用したいとします。その間、UI の応答性を維持し、他のことを行うことも必要です。電卓が終了したら、結果を表示します。

そしてもちろん、これには async / await を使用し、イベントフラグの設定やこれらのイベントが設定されるのを待つなどの古い方法は使用しません。

遅い計算機は次のとおりです。

private int SlowAdd(int a, int b)
{
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
    return a+b;
}

async-await の使用中にこれを非同期で使用する場合は、Task.Run(...) を使用して非同期で開始する必要があります。の戻り値Task.Runは、待機可能な Task です。

  • Task実行した関数の戻り値が void の場合
  • Task<TResult>実行した関数の戻り値がTResult

Task を開始するだけで、別のことを実行でき、入力した Task の結果が必要なときはいつでも await を実行できます。欠点が 1 つあります。

「待機」したい場合は、関数を非同期にして、Task代わりにvoidまたはTask<TResult>代わりに返す必要がありTResultます。

遅い電卓を実行するコードは次のとおりです。非同期関数の識別子を async で終了するのが一般的です。

private async Task<int> SlowAddAsync(int a, int b)
{
    var myTask = Task.Run ( () => SlowAdd(a, b));
    // if desired do other things while the slow calculator is working
    // whenever you have nothing to do anymore and need the answer use await
    int result = await myTask;
    return result;
}

補足: 一部の人々は、Start.Run よりも Task.Factory.StartNew を好みます。これについて MSDN が伝えていることを参照してください。

MSDN: Task.Run と Task.Factory.StartNew の比較

SlowAdd は非同期関数として開始され、スレッドが続行されます。答えが必要になると、タスクを待ちます。戻り値は TResult で、この場合は int です。

実行する意味がない場合、コードは次のようになります。

private async Task`<int`> SlowAddAsync(int a, int b)
{
    return await Task.Run ( () => SlowAdd(a, b));
}

SlowAddAsync は非同期関数として宣言されているため、この非同期関数を使用するすべての人も非同期で、Task または Task <TResult>を返す必要があることに注意してください。

private async Task UpdateForm()
{
     int x = this.textBox1.Text;
     int y = this.textBox2.Text;
     int sum = await this.SlowAddAsync(x, y);
     this.label1.Text = sum.ToString();
}

async / await の良いところは、前のタスクが完了するまで待つために ContinueWith をいじる必要がないことです。await を使用するだけで、タスクが終了し、戻り値があることがわかります。await の後のステートメントは、ContinueWith で通常行うことです。

ところで、Task.Run は関数を呼び出す必要はありません。ステートメント ブロックを配置することもできます。

int sum = await Task.Run( () => {
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
    return a+b});

ただし、別の関数の良い点は、非同期を必要としない/望んでいない/理解していない人に、非同期/待機なしで関数を使用する可能性を与えることです。

覚えて:

await を使用するすべての関数は非同期にする必要があります

すべての非同期関数は Task または Task <Tresult>を返す必要があります

「しかし、私のイベント ハンドラーはタスクを返すことができません!」

private void OnButton1_Clicked(object sender, ...){...}

その通りです。したがって、それが唯一の例外です。

非同期イベント ハンドラが void を返す場合がある

そのため、ボタンをクリックすると、非同期イベント ハンドラーによって UI の応答性が維持されます。

private async void OnButton1_Clicked(object sender, ...)
{
    await this.UpdateForm();
}

ただし、イベント ハンドラー async を宣言する必要があります。

<TResult多くの .NET 関数には、Task または Task >を返す非同期バージョンがあります。

- インターネット アクセス - ストリームの読み取りと書き込み - データベース アクセス - などの非同期関数があります。

それらを使用するには、Task.Run を呼び出す必要はありません。既に Task と Task を返し<TResultます。それらを呼び出すだけで、独自の処理を続行し、答えが必要な場合は Task を待って TResult を使用します。

複数のタスクを開始し、それらが終了するのを待つ 複数のタスク を開始し、それらすべてが終了するのを待ちたい場合は、Task.WhenAll(...) NOT Task.Waitを使用します

Task.Wait は void を返します。Task.WhenAll は Task を返すので、それを待つことができます。

タスクが終了すると、戻り値はすでに await の戻り値ですが、await の場合は Task.WhenAll ( new Task[]{TaskA, TaskB, TaskC}); タスクの結果を知るには、Task <TResult>.Result プロパティを使用する必要があります。

int a = TaskA.Result;

タスクの 1 つが例外をスローすると、AggregateException で InnerExceptions としてラップされます。したがって、Task.WhenAll を待機する場合は、AggregateException をキャッチし、innerExceptions をチェックして、開始したタスクによってスローされたすべての例外を確認する準備をしてください。関数 AggregateException.Flatten を使用して、より簡単に例外にアクセスします。

キャンセルについて読むのは興味深い:

マネージド スレッドでのキャンセルに関する MSDN

最後に: Thread.Sleep(...) を使用します。非同期バージョンは Task.Delay(TimeSpan) です。

private async Task`<int`> MySlowAdd(int a, int b)
{
    await Task.Delay(TimeSpan.FromSeconds(5));
    return a+b;
}

この関数を使用すると、プログラムの応答性が維持されます。

于 2015-07-29T07:46:01.440 に答える
1

私はこのサンプルコードに行き着きました...これはawaitableパターンの適切な実装ですか?

namespace CTP_Testing
{
using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

public class CustomAsync
{
    public static CustomAwaitable GetSiteHeadersAsync(string url)
    {
        return new CustomAwaitable(url);
    }
}

public class CustomAwaitable
{
    private readonly Task<string> task;
    private readonly SynchronizationContext ctx;

    public CustomAwaitable(string url)
    {
        ctx = SynchronizationContext.Current;
        this.task = Task.Factory.StartNew(
            () =>
                {
                    var req = (HttpWebRequest)WebRequest.Create(url);
                    req.Method = "HEAD";
                    var resp = (HttpWebResponse)req.GetResponse();
                    return this.FormatHeaders(resp.Headers);
                });
    }
    public CustomAwaitable GetAwaiter() { return this; }
    public bool IsCompleted { get { return task.IsCompleted; } }
    public void OnCompleted(Action continuation)
    {
        task.ContinueWith(_ => ctx.Post(delegate { continuation(); }, null));
    }
    public string GetResult() { return task.Result; }

    private string FormatHeaders(WebHeaderCollection headers)
    {
        var headerString = headers.Keys.Cast<string>().Select(
            item => string.Format("{0}: {1}", item, headers[item]));

        return string.Join(Environment.NewLine, headerString.ToArray());
    }
}
于 2011-08-07T18:52:41.443 に答える