質問者
Moniter.Enter()の処理に時間がかかる

質問
-
いつもお世話になっております。
Windowsアプリケーションで、クライアントからソケットで受信し、処理を行い、ソケットでクライアント
応答するというものを作りました。
このアプリケーションは、.NETFramework1.0(VisualStudio2002でビルド)で構築しました。このアプリケーションが稼働していたサーバが老朽のため、新しいサーバに移行しました。
【問題】
新しいサーバでこのアプリケーション(プログラムの修正なし)、実行したら、Moniter.Enter()の処理に時間がかかるという現象が発生しました。
クライアントからのソケット受信後のMoniter.Enterを行ったときに100ms~7s、処理に時間がかかるようです。
(Moniter.Enterがオブジェクトに対する相互排他を取得できない?)
頻度は、非同期で、時間がかからない(16ms以内)場合もあります。
20回に1回ぐらいの頻度で起きるようです。※旧サーバは、現象が発生しません。(5年間)
※社内のサーバは、現象が発生しません。
※64bitのサーバでは、現象が発生しません。※.NETFramework1.1、.NETFramework2.0でビルドしても現象は改善されませんでした。
※このプロセスのCPU数を限定してみましたが、この現象は改善されませんでした。
(CPU4個を指定、CPU2個を指定、CPU1個を指定でテストしました。)↓プログラムの概略を以下に記載します。
/**********************************************************************/
/// <summary>
/// スレッド管理クラス
/// </summary>
public class ThreadManager
{
public const int MAX_CLIENT_THREAD = 50; // 最大スレッド数
public static int THREAD_SLEEP_TIME = 500; // スレッドスリープ時間
public ClientRecive[] ClientRecive; // 受信処理クラス[n]
public Client[] Client; // クライアント処理クラス[n]/// <summary>
/// コンストラクタ
/// </summary>
public ThreadManager()
{
ClientRecive = new ClientRecive[MAX_CLIENT_THREAD];
Client = new Client[MAX_CLIENT_THREAD];for (int i = 0; i < MAX_CLIENT_THREAD; i++)
{
ClientRecive[i] = new ClientRecive(this, i);
Client[i] = new Client(this, i);
}}
}
/// <summary>
/// 受信処理クラス
/// </summary>
public class ClientRecive
{
public int Number; // クライアント番号
public Thread MyThread; // 自スレッド
private ThreadManager TMangaer; // スレッド管理クラス/// <summary>
/// コンストラクタ
/// </summary>
public ClientRecive(ThreadManager argTManager, int argNumber)
{
TMangaer = argTManager;
Number = argNumber;
MyThread = new Thread(new ThreadStart(Recive));
MyThread.Start();
}/// <summary>
/// 受信
/// </summary>
public void Recive()
{
string str = "";
int queueCount = 0;try
{
while (MyThread.IsAlive)
{
// 【・・・ソケットの受信処理】
// ↓の Monitor.Enterの処理で100ms~7s、処理に時間がかかるようです。
// 頻度は、非同期で、時間がかからない(16ms以内)場合もあります。20回に1回ぐらいの頻度で起きるようです。
Monitor.Enter(TMangaer.Client[Number].MyThread);TMangaer.Client[Number].Que.Enqueue(str);
queueCount = Que.Count();
if (queueCount <= 1)
{
TMangaer.Client[Number].SyncWaitor.Set();
}Monitor.Exit(TMangaer.Client[Number].MyThread);
}
}
catch (Exception exc)
{
// 【・・・処理】
}
finally
{
// 【・・・処理】
}}
}
/// <summary>
/// クライアント処理クラス
/// </summary>
public class Client
{
public int Number; // クライアント番号
public Thread MyThread; // 自スレッド
public ThreadManager TMangaer; // スレッド管理クラス
public AutoResetEvent SyncEvent; // 同期イベント
public Queue Que; // キュー
/// <summary>
/// コンストラクタ
/// </summary>
public Client(ThreadManager argTManager, int argNumber)
{
TMangaer = argTManager;
Number = argNumber;
SyncEvent = new AutoResetEvent(false);
Que = new Queue();
MyThread = new Thread(new ThreadStart(Execute));
MyThread.Start();
}/// <summary>
/// 実行
/// </summary>
public void Execute()
{
bool end = false;
string str = "";
int queueCount = 0;try
{
while (MyThread.IsAlive && end == false)
{
Monitor.Enter(ThreadManager.Client[Number].MyThread);
queueCount = Que.Count();
Monitor.Exit(ThreadManager.Client[Number].MyThread);if (queueCount == 0)
{
if (SyncEvent.WaitOne(ThreadManager.THREAD_SLEEP_TIME, false))
{
continue;
}
}while (end == false)
{
// キュー内のジョブがなくなるまで処理を繰り返す。Monitor.Enter(ThreadManager.Client[Number].MyThread);
queueCount = Que.Count();
Monitor.Exit(ThreadManager.Client[Number].MyThread);
if (queueCount == 0)
{
break;
}
else
{
str = Que.Peek();// 【・・・処理】
Monitor.Enter(ThreadManager.Client[Number].MyThread);
Que.Dequeue();
Monitor.Exit(ThreadManager.Client[Number].MyThread);
}}
}
}
catch
{
// 【・・・処理】
}
finally
{
// 【・・・処理】
}}
}/**********************************************************************/
【旧サーバのスペック】
CPU : インテル®E7501 インテル®Xeon®3.20GHzプロセッサ 1MB L3キャッシュ × 2 【2CPU】
メモリー : 4GB(4×1GB)
OS : Windows Server 2000 SP4【新サーバのスペック】(現象が起きるサーバ)
CPU : クアッドコア インテル®Xeon®プロセッサーE5420(2×6MB L2キャッシュ 2.50GHz 1333MhzFSB)× 2 【8CPU】
メモリー : 4GB(4×1GB 1R)667Mhz Fully Buffered DIMM ECC DRR2メモリ
OS : Windows Server 2003 R2 Standard Edition with SP2 日本語版【社内サーバのスペック】
CPU : クアッドコア インテル®Xeon®プロセッサーX5365 @3.00GHZ
メモリー : 4GB
OS : Windows Server 2003 R2 Standard Edition with SP2 日本語版【64bitのサーバのスペック】
CPU : クアッドコア インテル®Xeon®プロセッサーE5420(2×6MB L2キャッシュ 2.50GHz 1333MhzFSB)× 2 【8CPU】
メモリー : 4GB(4×1GB 1R)667Mhz Fully Buffered DIMM ECC DRR2メモリ
OS : Windows Server 2003 R2 Standard Edition with SP2 日本語版(64bit)
このような現象の理由と解決方法を、ご存知の方がいましたら教えて頂きたいです。
宜しくお願いします。
すべての返信
-
他の環境でも発生しているものの見逃しているだけの可能性もありますね。
リファクタリングは目的ではないのでしょうが、こういう場合、Monitor.Pulseメソッド を使うようですね。ちょうどサンプルコードもQueueクラスを使っています。
本題ですが、Threadクラスは内部でlock(this)を使用しています。つまりその処理の最中には「Monitor.Enter(ThreadManager.Client[Number].MyThread);」は待たされます。ただしそのThreadクラスの内部処理が7秒かかるとは思えないのでこれは直接の原因ではないと思います。