その場合、原因のスレッドを突き止めて終わらない原因を修正しなければいけません。
しかし、開発環境では再現できないケースなどもあると思います。その場合のデバッグ用クラスを作ってみました。
基本的に、"new Thread"を"TraceableThread.CreateThread"に置換して、メインウインドウの
FormClosedイベントあたりでTimerProcTraceを呼んであげるだけです。
そうすると、指定ミリ秒毎に終了していないスレッドがある場合、作成時のスタックトレース付きでデバッグ出力に出します。
開発環境が入っていない(入れられない)場合でも、DebugViewなどで確認できます。
VS2008で作りましたが、多分.NET2.0以降なら動くと思います。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Reflection;
namespace Diagnostics
{
/// <summary>
/// スレッドの追跡を行えます。
/// 終了していないスレッドを調べるのに役立ちます。
/// </summary>
public static class TraceableThread
{
private static List<Thread> m_listThread;
private static List<StackTrace> m_listStackTrace;
private static Timer m_timerRemove;
private static Timer m_timerTrace;
private static Object m_sync;
/// <summary>
/// 静的コンストラクタ
/// </summary>
static TraceableThread()
{
// 初期化
m_listThread = new List<Thread>();
m_listStackTrace = new List<StackTrace>();
m_sync = new Object();
// スレッド終了監視タイマー
m_timerRemove = new Timer(new TimerCallback(TimerProcRemove), null, 1000, 1000);
}
/// <summary>
/// スレッドの終了を監視し、スレッドリストを更新します。
/// </summary>
private static void TimerProcRemove(Object obj)
{
lock (m_sync)
{
List<Thread> remove = new List<Thread>();
// 終了しているスレッドを探す
foreach (Thread trd in m_listThread)
{
try
{
if (trd.Join(0) == true)
{
remove.Add(trd);
}
}
catch (ThreadStateException)
{
// スレッドが開始されていない場合
continue;
}
}
// リストから削除する
foreach (Thread trd in remove)
{
int nIndex = m_listThread.IndexOf(trd);
m_listThread.RemoveAt(nIndex);
m_listStackTrace.RemoveAt(nIndex);
}
}
}
/// <summary>
/// new Thread(ParameterizedThreadStart start)の代わりに呼んでください
/// </summary>
public static Thread CreateThread(ParameterizedThreadStart start)
{
StackTrace trace = new StackTrace(true);
Thread thread;
lock (m_sync)
{
m_listStackTrace.Add(trace);
thread = new Thread(start);
m_listThread.Add(thread);
}
return thread;
}
/// <summary>
/// new Thread(ThreadStart start)の代わりに呼んでください
/// </summary>
public static Thread CreateThread(ThreadStart start)
{
StackTrace trace = new StackTrace(true);
Thread thread;
lock (m_sync)
{
m_listStackTrace.Add(trace);
thread = new Thread(start);
m_listThread.Add(thread);
}
return thread;
}
/// <summary>
/// new Thread(ParameterizedThreadStart start, int maxStackSize)の代わりに呼んでください
/// </summary>
public static Thread CreateThread(ParameterizedThreadStart start, int maxStackSize)
{
StackTrace trace = new StackTrace(true);
Thread thread;
lock (m_sync)
{
m_listStackTrace.Add(trace);
thread = new Thread(start, maxStackSize);
m_listThread.Add(thread);
}
return thread;
}
/// <summary>
/// new Thread(ThreadStart start, int maxStackSize)の代わりに呼んでください
/// </summary>
public static Thread CreateThread(ThreadStart start, int maxStackSize)
{
StackTrace trace = new StackTrace(true);
Thread thread;
lock (m_sync)
{
m_listStackTrace.Add(trace);
thread = new Thread(start, maxStackSize);
m_listThread.Add(thread);
}
return thread;
}
/// <summary>
/// スレッドの終了追跡を開始します。
/// メインウインドウのFormClosedイベントで呼び出すことで、
/// 終了していないスレッドを検出することができます。
/// </summary>
public static void TraceThreadTermination(int interval)
{
if (m_timerTrace == null)
{
m_timerTrace = new Timer(new TimerCallback(TimerProcTrace), null, interval, interval);
}
}
/// <summary>
/// 終了していないスレッドをTraceに出力します。
/// </summary>
private static void TimerProcTrace(object obj)
{
lock (m_sync)
{
for (int i = 0; i < m_listThread.Count; i++)
{
Trace.WriteLine("");
Trace.WriteLine(DateTime.Now.ToLongTimeString());
Trace.WriteLine("■終了していないスレッドを検出しました!");
Trace.WriteLine("Name = " + m_listThread[i].Name + ", ManagedThreadId = " + m_listThread[i].ManagedThreadId.ToString());
Trace.WriteLine("スレッド作成時のスタックトレースは次の通りです。");
Trace.WriteLine(m_listStackTrace[i].ToString());
}
}
}
}
}

0 件のコメント:
コメントを投稿