Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

3,854 views

Published on

Published in: Technology
  • Be the first to comment

.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 を多用しない(初心者にありがちなミス)現実の開発では、追跡とデバッグが非常に困難 (例 : 単一では動作するんだけど ?、コンソール・アプリでは動くのに ?など)

×