kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

月別アーカイブ: 11月 2013

async/await はTaskには依存してないです


(追記)mataliloさんに await は依存してないけど async はっ?てツイッターで指摘されて気づきました。async は依存してます。メソッドの内容を Task に包みます。async はそういう意味で現状存在するTaskという選択肢にデフォルトで依存しています。単純に試した範囲内で同一名前空間、同名のクラスに+いくらかのメソッドというレベルの実装では置き換えできませんでした。すいません。(/追記)

 

F#の立ち位置 – 猫とC#について書く代わりにHaskellとF#について書くmatarilloの日記 – haskell

を読んで、色々とちょっと待ってよ感があったので書いてみます。

C#は色々詰め込みすぎってのはまぁそう。色々詰め込みすぎた結果として直行性を失ってる部分もある事はある。

ラムダの中で yield できないとかそういうのは確かに直行性を失ってる。catch ブロック内で async 使えないとかもその通り。

んでもね、async/await が Task にべったりくっついてて剥がしようがないってのは間違った話なんでそれを元に話が膨らんでいくのはちょっと待てなわけです。

 

さて、async/await を適用する場合の要件とかは以下の記事で書いてある通りです。自分が読む限りで間違いはありません。

連載:C# 5.0&VB 11.0新機能「async/await非同期メソッド」入門(最終回):第3回 非同期メソッドの内部実装とAwaitableパターンの独自実装 (2/2) – @IT

bleis さんは 並列/並行基礎勉強会でasync/awaitをDisってきた – ぐるぐる~ で「現状の async/await が Task と結びついているのは、Awaitable パターンが要求するシグネチャに戻り値の型に対する規定がないことから来ています。 つまり、Awaitable パターンを実装する Awaitable/Awaiter に「Task」が出てこないのに、裏で勝手に「Task」にラップされてしまうのです。」と書いてますが、そんな事は起こりません、やられていません。

順を追って確認しましょう。

メソッドの戻り値型であるTaskは Awaitable として返された物で Awaitable の利用方法である Awaiter の取得に答えます。要するにメソッドの戻り値として素直に戻り何もラップ等はされません。TaskはAwaitableとして利用するのに必要十分なメソッド(GetAwaiter)を実装していますのでそのようなラップ操作も必要ありません。 (Task<TResult>.GetAwaiter Method)

Task を返しているのはメソッドその物で、C#は GetAwaiter およびその後のAwaiterに言語仕様でのawaiterを満たすメンバが存在する為 await が適用可能であると判定しているだけです。

さて、await が適用されるとC#は Task の GetAwaiter を呼びだすコードを生成します。TaskのGetAwaiter はランタイムライブラリの TaskAwaiter<T> (System.Runtime.CompilerServices namespace)を返します、TaskAwaiter は Task を Awaiter としてラップする為のメソッドを実装しています。 .NET Framework ランタイムライブラリが Task を Awaiter としてTaskAwaiter でラップしているのであり Task でラップされているのではありません。そしてC#は TaskAwaiter を言語仕様で決めた通りに Awaiter として利用しているだけです。

C#言語はもう限界かもといってる文脈で出てくる事柄が何を何がラップするのかのされるの係り受けが違うし、C#言語でなくランタイムが主体だし、勝手にと言ってる事がメソッドシグネチャ通りにTaskAwaiter<T>を返したりって事で、問題の一文はぶっちゃけマルっと違います。

 

そして、TPL が C# での async/await に便宜を計ったのは Task のGetAwaiter ただ一点これだけで async/await の使い方でよく出てくる ContinueWith その他は async/await 登場前の C# 4.0 / .NET 4.0 から存在します。(Task クラス (System.Threading.Tasks) – .NET Framework 4.0) 、async/await の為にランタイムライブラリに追加されたのは TaskAwaiter であり Task を Awaiter として扱う為にラップします。

このように TPL とランタイムライブラリの間も過度に依存しないように実装されており、TPLの寿命という問題でC#言語の言語仕様そのものは揺るがないでしょう(ユーザーコードはいっぺんにTPLと共に寿命を迎えてしまう事になりますが)。C#の言語仕様そのものは TPL には全く依存性は無く、TPLとランタイムの間は C# 言語仕様での async/await のサポートの為に必要最小限に近い良く設計された依存性を持っていると思います。

 

主語がC#言語なのかランタイムなのか、TaskでラップするのかTaskがAwaiterにラップされるのか裏で勝手になのかシグネチャ通りやんなのか、数点の違いですが違うものは違いますという事で、盛り上がってる人達には落ち着け落ち着けと。んで、話題上で関連するランタイムとTPLがべったりくっついてて剥がしようがないとかもなく、良く設計された依存性でつながっている様に自分には見えますって事で、妄想をマジ受けし過ぎなんじゃないでしょうか。

んでC#は直行性とか一貫性を失ってる部分も無いわけじゃないけど、C#がダメだの限界だとかの話は根っこの理解違ってての話が広まるにも程があるだろ程度には看過しがたい状態かなと