.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)

3,548 views

Published on

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,548
On SlideShare
0
From Embeds
0
Number of Embeds
661
Actions
Shares
0
Downloads
0
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)

  1. 1. .NET Web プログラミングにおける非同期 I/O のすべて日本マイクロソフト株式会社エバンジェリスト松崎 剛http://blogs.msdn.com/b/tsmatsuz
  2. 2. 2セッション ゴール• .NET Web 開発における非同期 IO の基本を学ぶ発想の原点や歴史、基本メカニズム、用語などについて• べし・べからず、Tips、などを理解新機能にはワケがある !~ スケーラブルな Web を作ろう ~
  3. 3. 3非同期のプログラミング・パターン• EAP (Event-based Asynchronous Pattern)• APM (Asynchronous Programming Model)• TAP (Task-based Asynchronous Pattern)FileStream fs;byte[] readArray = new byte[0x1000];. . .fs.BeginRead(readArray, 0, readArray.Length,new AsyncCallback(readCallback), fs);. . .private void readCallback(IAsyncResult ar){System.IO.FileStream fs =(System.IO.FileStream)ar.AsyncState;int fsize = fs.EndRead(ar);. . .}MyReadClass myRead = new MyReadClass();myRead.ReadCompleted +=new EventHandler<ReadCompletedEventArgs>(readCompleted);myRead.ReadAsync();. . .private void readCompleted(object sender, ReadCompletedEventArgs e){var res = e.Result;. . .}using (StreamReader reader = File.OpenText(filename)){result = new char[reader.BaseStream.Length];Task<int> t = reader.ReadAsync(result, 0, (int) reader.BaseStream.Length);}
  4. 4. 4ASP.NET における非同期の変遷ASP.NET Web フォーム ASP.NET MVC.NET 2.0.NET 3.5.NET 4.5.NET 4.0
  5. 5. 5プログラミング・パターンの相性protected void Page_Load(object sender, EventArgs e){if (!IsPostBack){this.PreRenderComplete +=new EventHandler(Page_PreRenderComplete);AddOnPreRenderCompleteAsync(new BeginEventHandler(BeginAsyncOperation),new EndEventHandler(EndAsyncOperation));}}IAsyncResult BeginAsyncOperation(objectsender, EventArgs e, AsyncCallback cb, object state){_connection = new SqlConnection(connectstring);_connection.Open();_command = new SqlCommand("SELECT title_id, title, price FROM titles",_connection);return _command.BeginExecuteReader(cb, state);}void EndAsyncOperation(IAsyncResult ar){_reader = _command.EndExecuteReader(ar);}protected void Page_PreRenderComplete(objectsender, EventArgs e){Output.DataSource = _reader;Output.DataBind();}ASP.NET Async Page (APM)+DB Access (APM)
  6. 6. 6TAP + async/await (C# 5.0)public Task<ActionResult> Test1(){// Step 1HttpClient cl = new HttpClient();Task<HttpResponseMessage> task = cl.GetAsync(@"http://heavyweb.cloudapp.net/");return task.ContinueWith(t =>{// Step 2ViewBag.ResultData = t.Result.ToString();return (ActionResult)View();});}public async Task<ActionResult> Test1(){// Step 1HttpClient cl = new HttpClient();HttpResponseMessage result = await cl.GetAsync(@"http://heavyweb.cloudapp.net/");// Step 2ViewBag.ResultData = result.ToString();return (ActionResult)View();}
  7. 7. 7ASP.NET における非同期要求 (Request)、キュー (Queue)、スレッド (Thread)Web サーバー (IIS)要求 (Request)キュー (Queue)処理中 …処理中 …処理中 …スレッド (Thread)スレッド プール
  8. 8. 8ASP.NET における非同期同期のケースお医者さん=スレッド (Thread)患者さん=要求 (Request)受付=キュー (Queue)
  9. 9. 9ASP.NET における非同期同期のケース
  10. 10. 10ASP.NET における非同期同期のケース空くのを待機 . . .
  11. 11. 11ASP.NET における非同期非同期のケース
  12. 12. 12ASP.NET における非同期非同期のケース
  13. 13. 13ASP.NET における非同期非同期のケース
  14. 14. 14I/O Completion Port (IOCP)• Windows が提供する機構• 1 つの IOCP は、1 つ以上のデバイス ハンドル (ファイル ハンドルなど) と関連• キューのメカニズムを使って、非同期 IO の完了をプログラムから検知• スレッドの状態 (待ち状態、リリース状態、など) を監視し、スレッドの実行数を自動で制御• IOCP でブロックされたスレッドは、いったん解放されて、LIFO のキューに入る (1 つのスレッドが継続して処理可能。可能な限りContext Switch を抑制)• Win32 API を提供• カスタムな制御が可能• Thread Pool API を使った I/O 処理、Timer処理 (Thread Pool Timer)で使用
  15. 15. 15 15
  16. 16. 16 16public class Handler1 : IHttpHandler{public void ProcessRequest(HttpContext context){if (context.IsWebSocketRequest){BetsHandler1 handler = new BetsHandler1();context.AcceptWebSocketRequest(handler.Receive);}else{context.Response.StatusCode = 400; //bad request}}. . .}public class BetsHandler1{public WebSocket webSocket;public async Task Receive(AspNetWebSocketContext context){webSocket = context.WebSocket;ArraySegment<byte> buf =new ArraySegment<byte>(new byte[2048]);while (true){WebSocketReceiveResult res =await webSocket.ReceiveAsync(buf,System.Threading.CancellationToken.None);if (res.MessageType ==WebSocketMessageType.Close){// Close MessageconnectedHandlers.Remove(this);await webSocket.CloseOutputAsync(. . .);break;}else if (res.MessageType ==WebSocketMessageType.Text){// Text Message. . . Some kind of process}}}. . .}
  17. 17. 17 17public class Handler1 : IHttpHandler{public void ProcessRequest(HttpContext context){if (context.IsWebSocketRequest){BetsHandler1 handler = new BetsHandler1();context.AcceptWebSocketRequest(handler.Receive);}else{context.Response.StatusCode = 400; //bad request}}. . .}public class BetsHandler1{public WebSocket webSocket;public Task Receive(AspNetWebSocketContext context){webSocket = context.WebSocket;ArraySegment<byte> buf =new ArraySegment<byte>(new byte[2048]);while (true){WebSocketReceiveResult res =webSocket.ReceiveAsync(buf,System.Threading.CancellationToken.None);if (res.MessageType ==WebSocketMessageType.Close){// Close MessageconnectedHandlers.Remove(this);webSocket.CloseOutputAsync(. . .);break;}else if (res.MessageType ==WebSocketMessageType.Text){// Text Message. . . Some kind of process}}return new TaskFactory().StartNew(() => { });}. . .}
  18. 18. 18.NET 4.5 Web の TAP 対応 (“呼ぶ” 側)• ASP.NET Web フォーム• ASP.NET Web API• WCF• WebSocket. . .protected void Page_Load(object sender, EventArgs e){Page.RegisterAsyncTask(new PageAsyncTask(async () =>{HttpClient cl = new HttpClient();HttpResponseMessage res =await cl.GetAsync(@“http://.../");Label1.Text = res.ToString();}));}public class Service1 : IService1{public async Task<string> GetDataAsync(){HttpClient cl = new HttpClient();HttpResponseMessage res =await cl.GetAsync(@“http://.../");return res.ToString();}}public class ValuesController : ApiController{// GET api/valuespublic async Task<string> Get(){HttpClient cl = new HttpClient();HttpResponseMessage res =await cl.GetAsync(@"http://.../");return res.ToString();}}context.AcceptWebSocketRequest(handler.Receive);. . .public async Task Receive(AspNetWebSocketContext context){while (true){WebSocketReceiveResult res =await context.WebSocket.ReceiveAsync(. . .);. . .}}
  19. 19. 19IO リソースの TAP 対応 (“呼ばれる” 側)ファイル入出力 .NET 4.5 で TAP (async) のメソッド (ReadAsync, WriteAsync,CopyToAsync など) を提供 (これまでは、APM のみ)データベースADO.NET .NET 4.5 で TAP (async) のメソッド (ReadAsync など) を提供(これまでは、APM のみ)EntityFrameworkEntity Framework 6 で、TAP (async) をサポート(現在、ベータ版を提供)ネットワークREST HttpClient のメソッドは、基本的に TAP ベースWCF .NET 4.5 で自動生成される Service Refrence Proxy では、TAP(async) のメソッドを提供クラウド Windows AzureServiceBus最新の WindowsAzure.ServiceBus パッケージ (NuGet) で TAP のメソッド (NamespaceManager.QueueExistsAsync など) を提供(APM も使用可能)では、未対応のものはどうする ?(例 : WCF Data Services, Windows Azure Storage など)ますます、対応中 . . . (こうご期待!)
  20. 20. 20SynchronizationContext• スレッド間の関係を管理する抽象化されたスケジューラー・オブジェクト ASP.NET 非同期スレッドは、Win32 メッセージ ループのように特定スレッドに紐づかない• 1 つのスレッドに対し、必ず 1 つの SynchronizationContext が存在 (ただし、単一のSynchronizationContext は複数スレッドで共有)• 一部の実装 (override メソッド) を除き、具体的な実装は派生クラスに依存 WindowsFormsSynchronizationContext DispatcherSynchronizationContext AspNetSynchronizationContext 既定の SynchronizationContext• これまでの非同期処理 (EAP など) において、その動作をつかさどる• TAP では TaskScheduler を使用 (SynchronizationContext を使用する際は、TaskScheduler.FromCurrentSynchronizationContext を明示)
  21. 21. 21SynchronizationContext• 既定の Awaiter (TaskAwaiter) は、Current の SynchronizationContext を使用(なければ TaskScheduler も参照) AspNetSynchronizationContext では、同期ブロックに入れるスレッドは 1 つだけ• .NET 4 以降では、Task と相性の良い新しい AspNetSynchronizationContext を使用 従来のものは LegacyAspNetSynchronizationContext に変更この場合でも、Web.configの設定で新しいContextを使用可能<appSettings><addkey="aspnet:UseTaskFriendlySynchronizationContext“value="true"/></appSettings>
  22. 22. 22混ぜるな、危険 💀• Async (EAP, TAP, etc) と Sync の混在プログラムは、デッドロックの原因となる !Task で受け取った内容を、むりやり同期化しない (All async is beautiful !)「扱いやすい」(理解しやすい) という理由だけで、 Result、Wait を多用しない(初心者にありがちなミス)現実の開発では、追跡とデバッグが非常に困難 (例 : 単一では動作するんだけど ?、コンソール・アプリでは動くのに ?など)

×