コマンドラインプログラムで非同期処理ってどう書くの?

awaitableconsoleprogram

… ネタが降ってきた (;´Д`)

.NETのコンソールプログラムは、Visual Studioで普通に作ると、以下のようなテンプレコードが生成されます。

namespace ConsoleApplication2
{
	internal static class Program
	{
		public static int Main(string[] args)
		{
			return 0;
		}
	}
}

勉強会のasync/awaitシリーズで説明したシチュエーションは、WPFやWinFormsという環境での話でした。そこでは、「UIキュー」が重要で、このキューに非同期処理の完了が放り込まれ、メインスレッドがこれを拾い上げて処理する事で、await以降の処理が実行されることを説明しました。

コンソールで実行されるコードの場合、この「UIキュー」が存在しません(WPFやWinFormsのインフラを使った場合は生成されますが、ここでは扱いません)。そのため、await時に「ハードウェイト」しても困らない事になります。

UIキューが存在する環境でハードウェイトすると、UIキュー内のエントリが実行されなくなります。目に見える問題として、ユーザーの操作(ウインドウを移動したりサイズ変更したり、ユーザーインターフェイス要素の操作)が無視され、アプリケーションが固まっているように見えます。

しかし、コンソールアプリケーション実行時のウインドウ「コマンドプロンプト」は、アプリケーションのスレッドの状態に関わらず、常に操作可能です。以下のProcessExplorerのスクリーンショットは、コマンドプロンプトウインドウを激しく動かしている時に撮ったものです。

awaitableconsoleprogram3

コンソールアプリケーション(ここではcmd.exe)を起動すると、同時に子プロセスとして「conhost.exe」が起動します。はっきりしたことは分かりませんが、上記のようにウインドウの操作を行うと、このconhost.exeの負荷が上昇する事から、ウインドウメッセージの処理はこのプロセスが受け持っている事が推測できます。そうであれば、実際のコンソールアプリケーションは、UIキューの処理に影響される事無く(元々持っていない)、そしてユーザーインターフェイスがブロックされる事が無く、動作するのです。

話を戻して、コンソールアプリケーションではUIキューを持っていないので、スレッドがハードブロックしても問題ありません。Taskクラスを返却する「非同期メソッド」は、必要な個所でハードブロック出来ます。この事を利用して、以下のようなスケルトンコードを考える事が出来ます。

namespace ConsoleApplication2
{
	internal static class Program
	{
		private static async Task<int> InternalMainAsync(string[] args)
		{
			// あんな非同期やこんな非同期処理 ...

			// Console.WriteLineへのアクセスはスレッドコンテキスト違反にはならない
			Console.WriteLine("async/await on Console Application.");
		}

		public static int Main(string[] args)
		{
			try
			{
				return InternalMainAsync(args).Result;
			}
			catch (Exception ex)
			{
				Console.Error.WriteLine(ex.ToString());
				return Marshal.GetHRForException(ex);
			}
		}
	}
}

Mainの最初の処理として、InternalMainAsyncメソッドを呼び出します。このメソッドはTaskクラスを返却する非同期メソッドです。そのため、このまま放置すると次の処理に行ってしまいます(Mainを抜けて終了してしまう)。そのため、Waitを呼び出してハードブロックする必要があります。上記の例ではResultプロパティを呼ぶことで、間接的にハードブロックしています(戻り値intを取得するため)。

一方、InternalMainAsyncの中の処理は全て非同期で記述できます。ここでの知識は、WPFやWinFormsで非同期処理を記述する時と全く同じ知識を活用できます。言い換えると、普段から非同期処理を書いているなら、同じ流れで書いて問題ないという事です。

スレッドはどうなるのか? ですが、メインスレッドとUIキューの関係は無く、メインスレッドはResultでハードブロックしているので、全ての非同期処理(非同期処理の継続操作)はワーカースレッドのコンテキストで駆動されます。

今話題にしているのはコンソールアプリケーションでしたね? 例えば、ワーカースレッドからConsole.WriteLineを呼び出しても問題なく出力出来ますね? 勿論、マルチスレッドアクセスが認められないクラスやメソッド、特定のスレッドコンテキストに紐づくクラス等を扱う場合は、スレッド同期やマーシャリング等の操作が必要になりますが、この辺の知識はTask.RunやThreadクラスを使って手動でワーカースレッドを作った時と同じように考えればOKです。

追記: 現代の話 (2020年)

ネタとしてはもうかなり前から出来るようになっていますが、C# 7.1以上を使用している場合は、以下のように直接MainメソッドがTaskを返すように書く事が出来ます:

namespace ConsoleApplication3
{
	internal static class Program
	{
		public static async Task<int> Main(string[] args)
		{
			// あんな非同期やこんな非同期処理 ...
		}
	}
}

ポイントは、

  • メソッド名はMainであること
  • 引数は従来通り、無しかstring[]であること
  • 戻り値の型がvoidの場合は従来通りの扱い、Task又はTask<int>の場合のみ、Mainを非同期メソッドとして認識

.NET Core/Frameworkのバージョンではなく、C#のバージョンによって使用可能になります。つまり、この機能は、コンパイラが判断して適切なコードを自動生成する事で実現しています。生成されるコードは、私たちが手動で行っていたのと同じように、Wait()やResultを呼び出してハードウェイトしている事になるため、UIキューを使うようなシチュエーションでは使えません。

プロ生ちゃんをひろっちゃう! – プログラミング生放送勉強会 第30回@名古屋ソフトウェアセンター

プログラミング生放送勉強会で「プロ生ちゃんをひろっちゃう!」というタイトルでセッションに登壇してきました。

WP_20141108_12_21_12_Pro

初めての参加で、まったりと聞こうと思っていたのに、何故か登壇席にいた… そういうセッションです (;´Д`) 一体どういうことなのか、良く分からない…

仕方が無いので、プロ生ちゃんベースに、いつもとは違う方向性でセッションしました。途中、スライドが見えづらいというリアルタイムなツッコミに動揺し、しかもデモしようとしたら、Azureのネットワーク障害で見せられないという、デモ失敗あるあるでガタガタな結果に orz

すいませんすいませんすいません(泣

精進出来るようにがんばります。

内容はざっくり、プロ生ちゃんサイトの壁紙ページをスクレイピングして、非同期で画像をダウンロードしながらWPFでデータバインディングして表示するという内容です。

スクレイピングするために、どうやってサイトのHTMLを調査するか、そして実際にスクレイピングする時に使えるライブラリや、HTMLの解析方法、ユーザーインターフェイスをスムーズにする為の非同期処理の概要をちりばめました。

以下にスライドを置いておきます。また、例によってソースコードはGitHubに上げてあるので、見る事が出来なかった方は参考にして下さい。

スライド: プロ生ちゃんをひろっちゃう!.pptx
ソースコード: GitHub kekyo/Pronama.ScrapingViewer

メタプログラミングでEXCEL仕様書よ、さらば! – 第一回 Center CLR勉強会

BlogImage

新しいコミュニティ「Center CLR」を立ち上げました。今回はその第一弾となる勉強会で、登壇してきました!
当日は、生憎の雨模様でしたが、エントリーいただいた全ての方が出席いただいて感謝しています。

centerclr-p1

この勉強会は、中部圏での.NET Framework中核技術であるCLRをネタに展開して行こうと思っています。一応セッション形式の進行ですが、セミナーではなく勉強会なので、随時質問やところどころの脱線など、Welcomeでやっていく予定です。初めての方もお気軽にどうぞ!

centerclr-p4

次回勉強会の開催はまだ未定ですが、ご興味ある方は、Center CLR公式サイト(Facebookページ)や、DoorKeeperをチェックしてみて下さい。また、私のtwitterや公式twitterアカウント「@centerclr」でもつぶやきます。


さて、私のセッションは、「メタプログラミングでEXCEL仕様書よ、さらば!」という題目で、仕様書に対してEXCELを使用することの是非と、CLRで何が改善できるのか?という事をテーマに解説しました。出席者からもいろいろな質問を上げて頂き、良い勉強会になったかなと思います。

普段、あまりなじみが無いと思われる技術を掘り下げたため、セッション時間が2時間越えとなってしまいましたが、どうにか形になってほっとしています。

解説中のサンプルコードは、私のGitHubから参照可能です(セッション中に発見したコメントは、このブログ掲載時にはまだ修正していませんが、すぐに修正します)。また、セッションのプレゼンは、以下に置いておきます。

メタプログラミングでEXCEL仕様書よさらば10.pptx

それでは、また!