2007年06月26日

スレッド内で発生した例外 まとめ

   このエントリーをはてなブックマークに追加 Clip to Evernote
かなり回りくどい書き方をしてきたが、まとめれば以下の通り。
(ThreadPool.QueueUserWorkItemとDelegateのコードは省略)

○スレッド内で未処理の例外がある場合の動作         
Thread                 プログラム強制終了         ○
ThreadPool プログラム強制終了 ○
Timer プログラム継続 × 例外は行方不明
BeginInvoke プログラム継続 × 例外は行方不明
BeginInvoke+EndInvoke プログラム強制終了 ○

○×は、AppDomain.UnhandledExceptionで処理できるかどうか。

結論は、スレッド内で発生した例外は、そのスレッド内で処理しろってこと。
AppDomain.UnhandledException で処理させようとしても、うまくいかない。
もっとも、プログラムが終了してしまってよいのならば、UnhandledException使っても良いけどね。  

Posted by gushwell at 20:59Comments(2)TrackBack(0)

2007年06月25日

スレッド内で発生した例外 - System.Timers.Timer

   このエントリーをはてなブックマークに追加 Clip to Evernote
では、System.Timers.Timerを使った非同期処理の場合はどうなるんだろうか?
この Timerもスレッドが生成されるので、同じ動きだろうか?

public static void Example() {
System.Timers.Timer tm = new System.Timers.Timer();
tm.Elapsed += new System.Timers.ElapsedEventHandler(tm_Elapsed);
tm.Interval = 1000;
tm.Enabled = true;
Console.ReadLine();
Console.WriteLine("end");
}

static void tm_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
try {
Console.WriteLine("Hello World!");
Thread.Sleep(1010);
throw new Exception("1");
} finally {
Thread th = Thread.CurrentThread;
Console.WriteLine(
th.ManagedThreadId + " " +
th.IsThreadPoolThread);
}

}


というコードで試してみる。
あえて、tm.Interval よりも、em_Elapsed メソッドの方が時間がかかるようにしてある。

これを実行すると、今までのプログラムと違って、強制終了されない。
このコードを動かすと、当然だが、IsThreadPoolThread は、常に trueだ。
一方、ManagedThreadId は、4,5,6の値が何度も表示され、7以上の値が表示されることはない。

スレッド内で例外を処理しなくても、スレッドが自動的にスレッドプールに返却されているようだ。

さらに、興味深い?のは、AppDomain.UnhandledExceptionで例外がキャッチできないことだ。tm_Elapsed 内で未処理の例外があると、この例外は行方不明になってしまう。

これまでの結論は、AppDomain.UnhandledExceptionで は使えないってこと。


それと、繰り返しになるが、Timerの場合は、例外をキャッチしなくてもプログラムが強制終了してしまうことは無い。
一方、Threadの場合は、強制終了されてしまう。
この2つの違いは、スレッドプールと、通常のマニュアルスレッドの違いから来るのかな?
それとも、Timer独自の仕様なのだろうか?

ThreadPool.QueueUserWorkItem や DelegateのBeginInvoke についても調べないといけないな。
  
Posted by gushwell at 21:30Comments(0)TrackBack(0)

2007年06月23日

メルマガ:C#プログラミングレッスン 1200名突破

   このエントリーをはてなブックマークに追加 Clip to Evernote
購読者数が1200名を突破しました。
読者の方、本当にありがとうございます。

さて、現在はTraceについて書いていますが、当初は4,5回で終わるかなと考えていたのですが、まだ、カスタムリスナーの作り方や、TraceSource, Filterなどの説明が残っているので、長期連載の予感がしてきました。

log4netなどのツールを使っている方も多いと思いますが、いろいろな理由でこういったツールを利用できない方もいることと思います。
そのような方は、是非この連載を最後まで読んで、Traceの奥深さを知り、実際のプログラムで役立てて欲しいと思います。

Traceはいいから、別の話題にして! という方も多いかと思いますが、もうしばらくお付き合いください。

このTraceクラスは、自分でライブラリを作成する際にも、参考になることが多いですよ。

Traceの説明が終わったら、スレッドかリフレクションの説明でもしようかなと思います。
ただ、この2つ本格的に説明しようとすると、かなりの分量になりそうなので、こちらは、基本的なことに絞ろうかと思います。 

C#3.0の説明も、そのうちやろうと思いますが、僕自身がまだ十分に理解していないので、いつになることやら...


  
Posted by gushwell at 21:44Comments(0)TrackBack(0)

2007年06月22日

スレッド内で発生した例外 その2

   このエントリーをはてなブックマークに追加 Clip to Evernote
前回「スレッド内で発生した例外」の続き。
どうも気になったので調べてみた。
スレッドで例外が発生し、その例外を処理しないと、スレッドは終了せず、Runningの状態のままみたい。

となると、AppDomain.UnhandledException で例外を処理できたとしても、迷子になったスレッドが残ってしまい、スレッドを沢山起動するようなプログラムの場合は、問題が隠されてしまうだけで、本当の問題解決にはならないってことだな。

だから、スレッドで処理されない例外がある場合は、システム側で処理を強制終了させてしまうという仕様になってるんだろう。

と理解した。

MSDNライブラリを調べてみると、「マネージ スレッドの例外」には、それらしいことが書いてあった。

けど、事前にコードを書いて確かめてたから、少しは理解できたけど、日本語読解力が不足しているせいか、僕には、どうもピンとこない文章だ。

----
.NET Framework Version 2.0 では、共通言語ランタイムはスレッド内のほとんどの未処理の例外をそのまま続行させます。ほとんどの場合、これは未処理の例外によって-アプリケーションが終了することを意味します。
---
はまだ良いけど、

---
マネージ コードが例外ハンドラをインストールする機会を得る前に、ランタイムは未処理の例外をスローできます。マネージ コードにこのような例外を処理する機会がない場合でも、例外を続行させることができます。
---
になると?だ。
  
Posted by gushwell at 23:34Comments(0)TrackBack(0)

2007年06月21日

スレッド内で発生した例外

   このエントリーをはてなブックマークに追加 Clip to Evernote
昨日の記事「AppDomain.UnhandledException」の続き。

では、コンソールアプリで、スレッドを利用した場合の例外処理ってどうなるのだろうか?

public class Test {

[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAp
public static void Example() {
AppDomain currentDomain = Thread.GetDomain();
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandle

Thread th = new Thread(MyThread);
th.Start();
Console.ReadLine();
Console.WriteLine("end");
}

static void MyThread() {
Console.WriteLine("Thread 開始");
throw new Exception("my thread error");
}

static void MyHandler(object sender, UnhandledExceptionEventArgs args) {
Exception e = (Exception)args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
}

public static void Main() {
try {
Example();
} catch ( Exception ex ) {
Console.WriteLine("処理されていない例外: " + ex.Message);
}
}
}

というコードで試してみる。
UnhandledExceptionイベントハンドラは処理されるが、スレッド内で未処理の例外があるということで、昨日のコードと同様、プログラムが強制終了してしまう。

やはり、UnhandledExceptionで例外を処理するのではなく、MyThread()内で、try-catch を使い、例外処理をする必要があるようだ。  
Posted by gushwell at 21:08Comments(0)TrackBack(0)

2007年06月20日

AppDomain.UnhandledException

   このエントリーをはてなブックマークに追加 Clip to Evernote
どうも、昔調べたときと、例外処理の動作が変わっているみたいだ。
なので、再度調べなおしてみる。

まずは、MSDNドキュメントの「AppDomain.UnhandledException イベント」のページにあるサンプルを参考に、以下のコードを書いてみた。(まあ、ほとんど同じコードだけど)


using System;
using System.Security.Permissions;

public class Test {

[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlAppDomain)]
public static void Example() {
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);

throw new Exception("2");

}

static void MyHandler(object sender, UnhandledExceptionEventArgs args) {
Exception e = (Exception) args.ExceptionObject;
Console.WriteLine("MyHandler caught : " + e.Message);
}

public static void Main() {
Example();
}
}

実行すると、確かに制御が MyHandlerに行っているので、一応UnhandledExceptionイベンの処理は出来ているのだが、その後がいけない。

例の

「問題が発生したため、ConsoleApplication1 を終了します。 ご不便をおかけして申し訳ありません。」

というダイアログが出て、これを閉じると、コンソールに、

「ハンドルされていない例外: System.Exception: 2 ...」

というメッセージが表示されてしまう。どこが悪いのかさっぱりわからない。これってバグですか?
しかたないので、

Main() メソッドで、try-catch で、すべての例外を捕まえることにした。

 public static void Main() {
try {
Example();
} catch ( Exception ex ) {
Console.WriteLine("処理されていない例外: " + ex.Message);
}
}

コンソールアプリなんだし、こっちのほうが素直なコードだな。
ということで、これ以上深入りするのは辞めた。
  
Posted by gushwell at 22:56Comments(0)TrackBack(0)

2007年06月08日

Binary形式のシリアライズとSorp形式シリアライズの速度

   このエントリーをはてなブックマークに追加 Clip to Evernote
Binary形式のシリアライズとSorp形式シリアライズの速度を比較したくて、テストコードを書いてみた。
その一部を掲載

 private static Stream XmlSerial(SampleClass obj) {
IFormatter formatter = new SoapFormatter();
Stream stream = new MemoryStream();
formatter.Serialize(stream, obj);
stream.Seek(0, SeekOrigin.Begin);
return stream;
}

private static SampleClass XmlDeserial(Stream stream) {
IFormatter formatter = new SoapFormatter();
SampleClass obj = (SampleClass)formatter.Deserialize(stream);
return obj;
}


※ BinaryFormatter もnewする部分を変えただけのコード。

これを for文でまわして、速度を比較したのだが、binary形式のほうが約4倍早かった。
閉じた世界でかつ速度が要求されるならば、やはりBinary形式かなあ。  
Posted by gushwell at 23:21Comments(0)TrackBack(0)

2007年06月06日

TableAdapterのベースクラス

   このエントリーをはてなブックマークに追加 Clip to Evernote
ずいぶん前だが、
C#のパーシャルクラスとデザイナが生成したコードについて考えてみる

という記事の中で、 「なちゃさん」から
---
TableAdapterは、デザイナでBaseClassプロパティで基底クラスを指定できる
--
ということを教えていただきました。

いままで確かめてみる機会がありませんでしたが、やっと利用する機会が出てきました。

これを使えば、いくつもある TableAdapter で同じコードを書く必要がなくなります。

以下、書いてみたコードの断片。


 public abstract class AbstractTableAdatpter : Component {
private const string FieldNameCommandCollection = "_commandCollection";

private SqlCommand[] CommandCollection {
get {
Type type = this.GetType();
FieldInfo field = type.GetField(FieldNameCommandCollection,
BindingFlags.NonPublic | BindingFlags.Instance);
SqlCommand[] array = (SqlCommand[])field.GetValue(this);
return array;
}
}

// 上記 CommandCollectionを使って、実装
// タイムアウト値の取得と設定を行う。
public int CommandTimeout {
get { ... }
set { ... ]
}
...
}

_adapter フィールドや、_connection フィールドも同じようにしてやれば、
いろんなことが共通化出来そうです。

ただ、まだ動かしていないので、正しく動くかどうかわかりません...
  
Posted by gushwell at 20:51Comments(0)TrackBack(0)