async&await-代替案のポーリング[終了]


15

c#5の準備が整ったので、Anders Heijsbergが昨日PDC10で発表した「Asynchrony」の2つの新しいキーワードの選択に影響を与えるための空きがあるようです。

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Eric Lippertは、現在の2つのキーワードの選択と、それらがユーザビリティ研究で誤解されている方法について説明しています。コメントには他にもいくつかの提案があります。

お願い-回答ごとに1つの提案、重複は削除されます。


ちなみに、「言語統合非同期プログラミング」はLIAPを提供しますが、LINQと同じように舌から転げ落ちることはありません;)
Benjol

1
その顕著な飛躍がない限り。
コンラッドフリックス

3
しかし、「Language-Integrated Asynchronous Runtime」という頭字語はうまくできています。
グレナトロン

これはトピックから外れている必要があります。
DeadMG

回答:


6

の意味/必要性について明確ではないことを考えると、私はそれについてasync本当に議論することはできませんが、置き換えるための私の最善の提案awaitは次のとおりです:

yield while (見て!新しいキーワードはありません)

これについてもう少し考えたことに注意してください。whileこの方法で再利用するのは良いアイデアかどうか疑問に思います-自然な傾向は後でブール値を期待することです。

(考える:良いキーワードを見つけることは、良いドメイン名を見つけることに似ている:)


+1と方法によって、あなたは... 7分で彼のブログエントリにコメントに私を打つ
注意を自己に-名前を考える

ただし、タスクが既に完了している場合、必ずしも実行を譲るわけではありません。しかし、あなたは常にタスクの完了を待っています(決して待っていません)。
アロングラネネク

@Allon がfalseのwhile(x) {...}場合、どちらのループ本体も実行する必要はありませんx
自己への注意-

@Note:には動詞はありませんwhile。あなたは動詞を追加する場合などdo、あなたが取得do {...} while (x)しに関係なく、xの(少なくとも1回)、本体を実行します、。あなたの提案はyield whileに非常に似ているようですdo whileが、動詞の実行の反対の保証があり、少し誤解を招くかもしれません(しかし、それほど大したことではありません)。私が一番嫌いなのyieldは、それがメカニズムの実装を暗示しているということです。async/ の全体的なポイントはawait、非同期スタイルを同期スタイルで記述することです。yieldその同期スタイルを破ります。
アロングラネネク

新しいキーワードは必ずしも悪いことですか?私が理解しているように、awaitキーワードはコンテキストによって認識されるため、必要に応じて「await」という名前のメソッドまたは変数を使用できます。ある程度、新しい機能に新しいキーワードを使用することは、複数のことを意味するために既存のキーワードを再利用するよりも混乱が少ないと思います。(誇張された例:dangermouse.net/esoteric/ook.html
ティムグッドマン

5

キーワードがないのはどうですか?

たいていの場合、非同期メソッドを呼び出すときに、その結​​果が必要であることをコンパイラに認識してもらいたいです。

Document doc = DownloadDocumentAsync();

それでおしまい。人々がこのことのキーワードを考えるのに苦労しているのは、それが「物事が完全に正常であるならあなたがすることをする」というキーワードを持つようなものだからです。これがデフォルトであり、キーワードは不要です。

更新

最初に、コンパイラが型推論を使用して賢くなり、何をすべきかを考えるべきだと提案しました。これについてさらに考えると、CTPの既存の実装はそのままにしておきますが、awaitキーワードを明示的に使用する必要がある場合を減らすために、いくつかの簡単な追加を行います。

次の属性を作成します[AutoAwait]。これはメソッドにのみ適用できます。これをメソッドに適用する1つの方法は、マークすることasyncです。しかし、手でそれをすることもできます、例えば:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

次に、asyncメソッドの内部で、コンパイラはの呼び出しを待機することを前提とするDownloadDocumentAsyncため、指定する必要はありません。そのメソッドを呼び出すと、自動的に待機します。

Document doc = DownloadDocumentAsync();

さて、「賢い」とを取得しTask<Document>たい場合、演算子を使用しますstart。これはメソッド呼び出しの前にのみ表示できます:

Task<Document> task = start DownloadDocumentAsync();

きちんとした、私は思う。現在、単純なメソッド呼び出しは、通常の意味を意味します。メソッドが完了するまで待機します。そしてstart、何か違うことを示しています。待つな。

asyncメソッドの外部に表示されるコードの場合、[AutoAwait]メソッドを呼び出すことができる唯一の方法は、プレフィックスをに付けることstartです。これにより、asyncメソッドに表示されるかどうかに関係なく、同じ意味を持つコードを書く必要があります。

それから私は貪欲になり始めます!:)

まず、asyncインターフェイスメソッドに適用したいです。

interface IThing
{
    async int GetCount();
} 

基本的に、実装メソッドはを返さなければならないTask<int>か、に互換性のあるものでなければならずawait、メソッドの呼び出し元は[AutoAwait]振る舞います。

また、上記のメソッドを実装するとき、次のように書きたいと思います。

async int GetCount()

したがってTask<int>、戻り値の型として言及する必要はありません。

また、asyncデリゲート型(結局、1つのメソッドを持つインターフェイスのようなもの)にも適用したいと思います。そう:

public async delegate TResult AsyncFunc<TResult>();

asyncデリゲートは持っています-あなたはそれを推測- [AutoAwait]行動。asyncメソッドから呼び出すことができ、自動的にawait編集されます(startそれを選択しない限り)。だからあなたが言うなら:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

それはうまくいきます。メソッド呼び出しではありません。タスクはまだ開始されていません-はタスクではありませんasync delegate。タスクを作成するための工場です。あなたは言うことができます:

Document doc = getDoc();

そして、それはタスクを開始し、それが終了して結果を与えるのを待ちます。または、あなたは言うことができます:

Task<Document> t = start getDoc();

このため、「配管」が漏れる1つの場所は、asyncメソッドへのデリゲートを作成する場合、async delegate型を使用することを知っている必要があるということです。ですから、代わりに、とFunc言う必要がありますAsyncFunc。ある日、そのようなことは改善された型推論によって修正されるかもしれませんが。

もう1つの質問は、通常の(非同期ではない)メソッドで開始すると言うとどうなるかです。明らかに、コンパイルエラーは安全なオプションです。しかし、他の可能性もあります。


これは暗黙的な変換で実行可能ですが、それ以外の場合はステートメントの左から右への評価が必要になります(ラムダを除き、コンパイラの通常の動作とは正反対です)。の使用を妨げるvar可能性があるため、私はまだこれに反対すると思います、潜在的にいくつかの長い明示的な型名を置き換える必要があり、また、awaitケースと誰かが誤って通常の同期メソッドの代わりに非同期メソッドを呼び出したケースとの間であいまいです。最初は直感的に見えますが、実際には最小限の驚きの原則に違反しています。
アーロンノート

@Aaronaught-なぜそれを使用できないのvarですか?私の答えの前の改訂版に応答しているのだろうか...私は完全に書き直した。この提案は次のように考えることができます:メソッドが特別な属性でマークされている場合、awaitキーワードがそのメソッドの呼び出しの前に自動的に挿入されるかのようになります(これをstartプレフィックスで抑制しない限り)。すべてがCTPとまったく同じであるため、var正常に機能します。
ダニエル・アーウィッカー

実際、私は...このスレッドを再訪して、あなたがそれを編集することにしたのとほぼ同時にあなたの答えに答えることにしたのは奇妙でした。今すぐ読み直さなくてはなりません…
アーロンノート

1
awaitキーワードの反転が気に入っています。また、の三重冗長性も嫌いですpublic async Task<int> FooAsync()
アロングラネネク

1
はい、Async-postfixの命名規則は、何かがより正式にキャプチャされる可能性があることを示しています。基本的に、「このようなメソッドは特定の方法で命名する必要があるため、人々はそれらを適切に呼び出す方法を知っている」というルールがある場合、同じルールを使用してそれらのメソッドを特定の方法で属性付けし、コンパイラがそれらを適切に呼び出すのに役立ちます。
ダニエル・アーウィッカー


4

async大丈夫だと思いますが、ASP.NET非同期ページに関連付けているからかもしれません-同じ考えです。

await私が好むキーワードのためにcontinue afterまたはresume after

セマンティクスはメソッドが実際に実行を決してもたらさないようなものyieldであるため、私好きではありません。タスクの状態に依存します。


が好きresume afterですawait。たぶんasync呼ばれるかもしれませんresumable
ティムグッドマン

ただ好むだけですがafter、このcontinue afterアプローチには強力な実装上の利点があります。現在の既存のコンテキストキーワードが含まれていますが、現在の使用法と互換性のない構文です。これにより、追加が既存のコードを壊さないことが保証されます。まったく新しいキーワードを使用する場合、実装は、古いコードの識別子として単語を使用する可能性に対処する必要があり、非常に注意が必要です。
エドゥルンパスクアル

3

エリックのブログにもコメントを追加しましたが、同じキーワードを使用しても問題は見られません async

var data = async DownloadFileAsync(url);

ファイルを非同期でダウンロードすることを表明しています。ここには少し冗長性があり、「非同期」はメソッド名にも含まれているため、2回表示されます。コンパイラは非常に賢く、「Async」で終わるメソッドが実際の非同期メソッドであるという規則を検出し、コンパイル済みコードに追加します。代わりに、あなたはただ電話したいかもしれません

var data = async DownloadFile(url);

同期的なものを呼び出すのとは対照的に

var data = DownloadFile(url);

また、宣言にasyncキーワードがあるため、同じ方法で定義できるはずです。なぜ各メソッド名に「非同期」を手動で追加する必要があるのか​​-コンパイラがそれを行うことができます。


私はあなたが追加した砂糖が好きですが、C#の連中はおそらくそれには向かないでしょう。(FWIW、属性名を検索するとき、すでに同様のことを行います)
自己への注意-

2
そしてasync、メソッドのキーワードが(私が正しく理解していれば)ちょうどいいものであることを考えると、あなたが提案するものと反対のことをするのが最良のことではないかと思います:asyncメソッドを放棄し、それを使用するだけです彼らが現在持っている場所await
ベンジョー

それは可能性です。メソッドdecarationのasyncキーワードは、本当にきれいにするためだけにあります。そのままにしておくことをお勧めしますが、メソッド名に「非同期」を追加する必要はありません。例えば(async Task<Byte[]> DownloadFile(...)むしろTask<Byte[]> DownloadFileAsync(...)後者はコンパイルされた署名になります)。どちらの方法でも機能します。
マークH

私も正直これのファンではありません。前のコメントのように、最終バージョンは実際に書かれたものとはまったく異なるメソッドを呼び出しており、そのメソッドの実装と署名は実装クラスに完全に依存しているため、最終バージョンは最小限の驚きの原則に違反していることを指摘する必要があります。最初のバージョンでさえ、実際に何も言っていないため、問題があります(この非同期メソッドを非同期で実行しますか?)。私たちが表現しようとしている考えは、継続的実行または延期された実行であり、これはそれをまったく表現していません。
アーロンノート

3

async = task-タスクを返すように関数を変更しているので、キーワード「task」を使用しないのはなぜですか?

await = finish-必ずしも待つ必要はありませんが、結果を使用する前にタスクを「終了」する必要があります。


ここでのシンプルさについて議論するのは本当に難しいです。
-sblom

2

私は好きyield untilです。 yield while、既に提案されている、素晴らしいし、新しいキーワードを導入しませんが、私は「まで」は少し良く行動をキャプチャすると思います。

yield <something>yieldはすでにメソッドの残りの部分を非常にうまく継続するというアイデアをキャプチャしているので、素晴らしいアイデアだと思います。誰かが「まで」よりも良い言葉を考えることができるかもしれません。


2

アーロンGの提案に対する投票を登録したいだけです。comefromこれは、INTERCALのCOMEFROMステートメントの最初の適切な使用です。アイデアは、コード内のある場所をCOMEFROMステートメントにジャンプせるという点で、GOTO の反対(GOTOステートメントからジャンプ)のようなものだということです。


2

Task<T>s を扱っているので、次のようにstart、ステートメントの前にキーワードとして使用するのはどうですか。

start var document = FetchAsync(urls[i]);


うーん、おそらくfinishより良いでしょうstartか?
主人公

1

F#asyncが非同期ワークフローでもキーワードを使用していることは注目に値します。これは、C#5の新しい非同期機能とほとんど同じです。したがって、私はそれを同じに保ちます

awaitF#のキーワードでは、let!代わりにを使用しますlet。C#には同じ割り当て構文がないため、=記号の右側に何かが必要です。Benjolが言ったように、それは同じように機能するyieldので、それのほとんど変形であるはずです。


1
「待機」は割り当て内にある必要はありません(もちろん、通常はそうです。)GetAwaiterを見つけることができる型を持つほとんどすべての式の演算子として正当です。(正確なルールはまだ公開可能な形式に組み込まれていません。)
エリックリッパー

1
だろうF#で@Eric、do!が、あなたはそれを知っていた...
Benjol

1

yield async FetchAsync(..)


これは、async呼び出すメソッドに配置する必要がある修飾子と完全に一致します。また、現在のセマンティック、yield returnつまり、戻ります、この場合、あなたは非同期メソッドに、あなたの実行を得ている間に列挙コードに利回りの実行。

将来、に他の用途があると想像してみてください。ほとんど同じことを実行するためにこれらすべての異なるキーワードを持たせる代わりに、xが光沢のある新機能であるをyield追加できyield xます。

率直に言って、私は「実行を譲らない」という議論をよく理解していません。結局のところ、別のメソッドを呼び出すポイントは、そのメソッドに「実行を譲り渡す」ことではないのでしょうか?非同期かどうかに関係なく?私はここで何かが欠けていますか?

async同期的に戻りますが、キーワードがある場合は、メソッドが非同期で実行される可能性が高く、実行を別のメソッドに譲る可能性があることを示す必要があります。メソッドは、メソッドが実際に非同期呼び出しを行うかどうかに関係なく、そのことを考慮する必要があります。

IMOさまざまな「譲れない」ケースは実装の詳細だと思います。言語の一貫性(つまり、再利用yield)を保証します。


0

complete「タスクを完了させたい」のように、どうですか?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;

1
なぜ下票なのですか?少なくとも、投票後、自分自身を説明するのは礼儀です。
アロングラネレク

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.