6

awaitAsync CTPキーワードと同様に機能するものをどのように実装しますか?awaitすべての場合のように機能する単純な実装はありますか、それともawaitシナリオごとに異なる実装が必要ですか?

4

6 に答える 6

9

新しいawaitキーワードは、既存のキーワードと同様のセマンティクスを持ちyield return、どちらもコンパイラーに継続スタイルのステートマシンを生成させます。したがって、非同期CTPと同じ動作のいくつかを持つイテレータを使用して何かを一緒にハックすることが可能です。

これがどのようになるかです。

public class Form1 : Form
{
    private void Button1_Click(object sender, EventArgs e)
    {
        AsyncHelper.Invoke<bool>(PerformOperation);
    }

    private IEnumerable<Task> PerformOperation(TaskCompletionSource<bool> tcs)
    {
        Button1.Enabled = false;
        for (int i = 0; i < 10; i++)
        {
            textBox1.Text = "Before await " + Thread.CurrentThread.ManagedThreadId.ToString();
            yield return SomeOperationAsync(); // Await
            textBox1.Text = "After await " + Thread.CurrentThread.ManagedThreadId.ToString();
        }
        Button2.Enabled = true;
        tcs.SetResult(true); // Return true
    }

    private Task SomeOperationAsync()
    {
        // Simulate an asynchronous operation.
        return Task.Factory.StartNew(() => Thread.Sleep(1000));
    }
}

yield returnを生成するため、IEnumerableコルーチンはを返す必要がありIEnumerableます。すべての魔法はAsyncHelper.Invokeメソッド内で発生します。これが、コルーチン(ハッキングされたイテレータになりすました)を実行するためのものです。awaitイテレータが存在する場合は、現在の同期コンテキストで常に実行されるように特別な注意が必要です。これは、UIスレッドでの動作をシミュレートするときに重要です。これは、最初のMoveNext同期を実行し、次にSynchronizationContext.Send、個々のステップを非同期で待機するためにも使用されるワーカースレッドから残りを実行するために使用することによってこれを行います。

public static class AsyncHelper
{
    public static Task<T> Invoke<T>(Func<TaskCompletionSource<T>, IEnumerable<Task>> method)
    {
        var context = SynchronizationContext.Current;
        var tcs = new TaskCompletionSource<T>();
        var steps = method(tcs);
        var enumerator = steps.GetEnumerator();
        bool more = enumerator.MoveNext();
        Task.Factory.StartNew(
            () =>
            {
                while (more)
                {
                    enumerator.Current.Wait();
                    if (context != null)
                    {
                        context.Send(
                            state =>
                            {
                                more = enumerator.MoveNext();
                            }
                            , null);
                    }
                    else
                    {
                        enumerator.MoveNext();
                    }
                }
            }).ContinueWith(
            (task) =>
            {
                if (!tcs.Task.IsCompleted)
                {
                    tcs.SetResult(default(T));
                }
            });
        return tcs.Task;
    }
}

についての全体的な部分は、値を「返す」ことができるTaskCompletionSource方法を複製する私の試みでした。await問題は、コルーチンIEnumerableハッキングされたイテレータにすぎないため、実際にを返さなければならないことです。そのため、戻り値を取得するための代替メカニズムを考え出す必要がありました。

これにはいくつかの明白な制限がありますが、これがあなたに一般的な考えを与えることを願っています。また、CLR、コルーチンを実装するための1つの一般化されたメカニズムをawaitどのように持つことができるかを示します。コルーチンはyield return、ユビキタスに使用されますが、それぞれのセマンティクスを提供するためにさまざまな方法で使用されます。

于 2011-07-15T19:48:52.040 に答える
8

await常に同じ種類の変換が含まれますが、それはかなり苦痛なものです。のライブラリawaitはそれほど複雑ではありませんが、トリッキーな点は、コンパイラがステートマシンを構築して、継続を適切な場所にジャンプできるようにすることです。

私のイテレータブロックのハッキーな使用(イールドリターン)は、似たようなものを偽造する可能性があります...しかし、それはかなり醜いでしょう。

数週間前にコンパイラが舞台裏で何をしているのかについてDevExpressウェビナーを行いました。これは、いくつかの例から逆コンパイルされたコードを示し、コンパイラが返すタスクを構築する方法と、「待機者」が何をしなければならないかを説明しています。行う。それはあなたに役立つかもしれません。

于 2011-03-23T06:27:50.740 に答える
2

イテレータ(yield)で作成されたコルーチンの実装と例はほとんどありません。

例の1つは、非同期GUI操作にこのパターンを使用するCaliburn.Microフレームワークです。ただし、一般的な非同期コードには簡単に一般化できます。

于 2011-03-23T06:32:52.603 に答える
2

MindTouch DReAMフレームワークは、機能的にAsync/Awaitと非常によく似たIteratorパターンの上にコルーチンを実装します。

async Task Foo() {
  await SomeAsyncCall();
}

対。

IYield Result Foo() {
  yield return SomeAsyncCall();
}

ResultDReAMのバージョンですTask。フレームワークdllは.NET2.0以降で動作しますが、最近は3.5構文を多く使用しているため、フレームワークをビルドするには3.5が必要です。

于 2011-05-28T19:54:31.917 に答える
1

MicrosoftのBillWagnerが、MSDN Magazineに、VisualStudio2010でタスク並列ライブラリを使用して非同期ctpへの依存関係を追加せずに非同期のような動作を実装する方法についての記事を書きました。

これはTask、 C#5がリリースされると、コードがandのTask<T>使用を開始する準備が整っているという追加の利点もあります。asyncawait

于 2011-11-17T08:38:47.030 に答える
0

yield return私の読書から、との間の主な違いは、継続に新しい値を明示的に返すことができるawaitということです。await

SomeValue someValue = await GetMeSomeValue();

一方、を使用するとyield return、参照によって同じことを実行する必要があります。

var asyncOperationHandle = GetMeSomeValueRequest();
yield return asyncOperationHandle;
var someValue = (SomeValue)asyncOperationHandle.Result;
于 2011-10-04T11:38:01.493 に答える