概要
あと少しでようやく終わる……。
流し読みノート
何がええねん非同期
- Windows の機能の多くは非同期的に動かせるのに、多くの場合は同期 API 経由で使われている。
- 同期 API で使うと、処理が完了するまでスレッドがブロックされてるのでムダなのだ。
- 言語レベルの非同期サポートは C# 5.0 から。
- てことはつまり、非同期処理を書くこと自体は昔からできたのだ。
// async await を使わないで非同期処理を書く。
// ヤバすぎ。
using System.Linq;
private void Foo(string url)
{
var w = new System.Net.Http.HttpClient();
var req = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Head, url);
var uiScheduler = System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext();
w.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseHeadersRead).ContinueWith(sendTask =>
{
try
{
System.Net.Http.HttpResponseMessage response = sendTask.Result;
var headerString = from header in response.Headers
select header.Key + ":" + string.Join(",", header.Value);
string headerList = string.Join(System.Environment.NewLine, headerString);
}
finally
{
w.Dispose();
}
}, uiScheduler);
}
async await を使うとどうなんねん
こうなんねん
using System.Linq;
private async void Bar(string url)
{
using var w = new System.Net.Http.HttpClient();
var req = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Head, url);
System.Net.Http.HttpResponseMessage response =
await w.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseHeadersRead);
var headerString = from header in response.Headers
select header.Key + ":" + string.Join(",", header.Value);
string headerList = string.Join(System.Environment.NewLine, headerString);
}
async
await
を使うと、コードが単純に書けて、非同期コードによる恩恵を簡単に得られる。async
はコードのコンパイル方法を決める。- ↑のコードを見ると、フツーのコード(同期コード)と同じじゃんと思うけれど、
await
ついているとスレッドをブロックしないのだ。
// ブロックする場合はこう。
System.Net.Http.HttpResponseMessage response =
w.SendAsync(req, System.Net.Http.HttpCompletionOption.ResponseHeadersRead).Result;
async await のうんちく
await
の行まで実行が進むと、「実行コンテキストがキャプチャ」される(よくわからん)。- 同じメソッド内で複数
await
があるときは、ひとつのコンテキストを使い回す。
- 同じメソッド内で複数
async
メソッドの返り値はvoid
Task
Task<T>
のみ。- 基本的には
Task
を返すべき。例外を補足できたり、よいことばかり。Task
を返すことで不便になることはほぼない。
- 基本的には
- 匿名メソッドも非同期にできる。
async delegate(object s, RoutedEventArgs e)
{
using (var w = HttpClient())
{
infoTextBlock.Text = await w.GetStringAsync(uriTextBox.Text);
}
}
- 非同期 API の多くは TPL(Task Parallel Library)タスクを返す。
- もちろん例外処理もできるよ。
try
{
string longest = await FindLongestLineAsync("http://localhost");
Console.WriteLine(longest);
}
catch (HttpRequestException x)
{
Console.WriteLine(x.Message);
}
async
メソッドの引数を検証するときはラッパーメソッドが要る。
public Task<string> FindLongestLineAsync(string url)
{
if (url == null)
{
throw new ArgumentNullException("url");
}
return FindLongestLineCore(url);
}
private async Task<string> FindLongestLineCore(string url)
{
// ...
}
小休憩
この話は Pythonista の末席を汚しまくる者としてはわりと関心がある。実は Python にも async
await
があるのよな。たしか3.5あたりから。