2

非同期CTPに関するいくつかの関連する質問:

  • と列挙子メソッドを使用して、イテレータブロック(IEnumerable<T>yield-returning T)を反復処理できます。メソッドのアナログは何ですか?非呼び出しメソッドでedアイテムを受信して​​処理するにはどうすればよいですか?簡単な例を教えてください。私はそれを見ていません。GetEnumerator()MoveNext()Current()asyncasyncawaitContinueWith()

  • また、この次の例のasyncメソッドでMyAwaitableは、GetAwaiter()メソッドがあります。GetAwaiter()を返すstringTHuhそうでない場合string、コンパイラは文句を言いません。THuhとの間にどのようなタイプの制約/期待が存在しGetAwaiter()ますか?

    async Task<THuh> DoSomething()
    {
         var x = await new MyAwaitable("Foo");
    
         var y = await new MyAwaitable("Bar");
    
         return null;
    } 
    
  • C#仕様ドラフトの次の行を説明してください。決して使用されないasync Task<T>メソッドが想定されreturnていますか?default(T)このルールに従わないように見えるサンプルがいくつかあります。戻り値は到達可能であり、値はデフォルトではありません。この値にアクセスできませんか?もしそうなら、なぜ厄介なアクセスできないreturnステートメントですか?

Task<T>一部のreturn型の非同期関数ではT、returnステートメントに暗黙的にに変換可能な式がT必要であり、本体のエンドポイントに到達できない必要があります。

  • 仕様には、「GetAwaiter、IsCompleted、OnCompleted、およびGetResultのすべてが「非ブロッキング」であることが意図されている」と記載されています。したがって、(潜在的に)長時間実行される操作をどのメソッドで定義する必要がありますか?

ありがとう!

4

2 に答える 2

7

Iterator Blocksで(ぎこちなくしかし成功して)継続渡しを変換し、代わりに非同期を使用するように変換しようとしています。私は新しい方法で戦っていると思います。変化を理解することは、ベルクロの3インチ幅のストリップを引き裂くように感じます。

あなたがそのように感じるかもしれないことを私は理解することができます。イテレータとCPSに共通する基本的なメカニズムに関係なく、実際には適切ではないため、イテレータブロックからCPSを構築しようとすることはお勧めしません。イテレータブロックは、データ構造をシーケンスに変換したり、シーケンスを別のシーケンスに変換したりするメソッドをすばやく作成できるように設計されています。これらは、現在の継続による呼び出しの一般的な問題を解決するようには設計されていません。

さらに言えば、async / awaitは、明らかに、桁違いに近いものの、正確に現在の継続を呼び出すわけではありません。Async / awaitは、タスクベースの非同期を容易にするように設計されています。コードを継続渡しスタイルの形式に書き換えることによってこれを行うことは、実装の詳細です。

私が関連トピックについて書いたこの回答は役立つかもしれません:

c#5.0の新しい非同期機能をcall / ccでどのように実装できますか?

あなたが抱えている概念的な問題は、イテレータスタイルの非同期では、「オーケストレータ」(イテレータブロックが中断したところから再開するときに把握するもの)がコードであるということだと思います。コードを記述し、MoveNextを呼び出してイテレーターをポンピングするタイミングを決定します。タスクベースの非同期では、他のコードの塊がそれを行います。タスクが完了すると、その事実がどこかのメッセージキューに投稿され、メッセージキューがポンプされると、結果とともに継続がアクティブ化される可能性が高くなります。コードには、ポイントできる明示的な「MoveNext」はありません。むしろ、タスクが完了し、それ自体の継続を知っているという事実は、継続が最終的な実行のためにワークキューに入れられることを保証するのに十分です。

さらに質問がある場合は、SOや非同期フォーラムに投稿することをお勧めします。

于 2011-12-09T01:00:42.730 に答える
1

あなたのDoSomething例では、MyAwaitableのGetResultメソッドのタイプはとは関係がないため、コンパイラは文句を言いませんTHuh。に関連するステートメントTHuhはですreturn null;。nullリテラルは暗黙的にに変換可能THuhであるため、すべて問題ありません。

IEnumerableに類似したキーワードはawaitですforeachawait特定のパターンに適合するタイプが必要であり、そうforeachです。1つは待機可能なタイプを消費するためのメカニズムであり、もう1つは列挙可能なタイプを消費するためのメカニズムです。

一方、イテレータブロック(yield returnおよび)は、列挙可能な型を定義するyield breakためのメカニズムです(型を明示的に宣言するのではなく、メソッドを記述します)。ここでの例えはキーワードです。async

asyncとの間の類似性を詳しく説明するために、戻り値のイテレータブロックには同様にステートメントを含めることができ、戻り値の非同期メソッドにはステートメントを含めることができることyield returnに注意してください。どちらの場合も、戻り式の型がメソッドの戻り型ではなく、メソッドの戻り型の型引数であることに注意してください。IEnumerable<int>yield return 42;Task<int>yield return 42;


まだ読んでいない場合は、これらのトピックに関するEricLippertのブログを読む必要があります。

http://blogs.msdn.com/b/ericlippert/archive/tags/Async/

http://blogs.msdn.com/b/ericlippert/archive/tags/Iterators/

また、Asyncシリーズ以外の継続渡しスタイルに関する投稿は、概念が(私にとっては)初めての場合に役立つ可能性があります。

http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+style/


最後に、例については、MSDNの記事と同じ問題の関連記事にリンクしているEricのブログ投稿と、Bill Wagnerによるフォローアップ記事(http://msdn.microsoft.com/en-us/vstudio/hh533273 )を参照してください。

編集

このルールに従わないように見えるサンプルがいくつかあります。戻り値は到達可能であり、値はデフォルトではありません。この値にアクセスできませんか?もしそうなら、なぜ厄介なアクセスできないreturnステートメントですか?

「本体の端点に到達できない必要がある」というフレーズは、returnステートメントが必要であることを意味します。本文のエンドポイントはreturnステートメントの後にあり、returnステートメントによって到達不能になります。通常のint-returningメソッドを使用した例:

public int Main()
{
    Console.WriteLine("X");
    //after this comment is the reachable end point of the body; this method therefore won't compile.
}

public int Main()
{
    Console.WriteLine("X");
    return 0;
    //anything after the return statement is unreachable, including the end point of the body; this method therefore will compile.
}

編集2

これは、文字列の後半を計算するウェイターの短くて些細な例です。この例では、結果をコンソールに出力する継続を渡します。スレッドセーフではありません!

public static class StringExtensions
{
    public static SubstringAwaiter GetAwaiter(this string s)
    {
        return new SubstringAwaiter(s, s.Length / 2, s.Length - s.Length / 2);
    }
}

public class SubstringAwaiter
{
    private readonly string _value;
    private readonly int _start;
    private readonly int _length;
    private string _result;
    private Action _continuation;

    public SubstringAwaiter(string value, int start, int length)
    {
        _value = value;
        _start = start;
        _length = length;
    }

    public bool IsCompleted { get; private set; }
    public void OnCompleted(Action callback)
    {
        if (callback == null)
            return;

        _continuation += callback;
    }
    public string GetResult()
    {
        if (!IsCompleted)
            throw new InvalidOperationException();
        return _result;
    }
    public void Execute()
    {
        _result = _value.Substring(_start, _length);
        IsCompleted = true;
        if (_continuation != null)
            _continuation();
    }
}

public class Program
{
    public static void Main()
    {
        var awaiter = "HelloWorld".GetAwaiter();
        awaiter.OnCompleted(() => Console.WriteLine(awaiter.GetResult()));
        awaiter.Execute();
    }
}
于 2011-12-08T18:20:15.567 に答える