マルチスレッドTCPサーバーでインターロックされたMonitor.WaitとMonitor.Pulseに問題があります。私の問題を示すために、ここに私のサーバーコードがあります:
public class Server
{
TcpListener listener;
Object sync;
IHandler handler;
bool running;
public Server(IHandler handler, int port)
{
this.handler = handler;
IPAddress address = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
listener = new TcpListener(address, port);
sync = new Object();
running = false;
}
public void Start()
{
Thread thread = new Thread(ThreadStart);
thread.Start();
}
public void Stop()
{
lock (sync)
{
listener.Stop();
running = false;
Monitor.Pulse(sync);
}
}
void ThreadStart()
{
if (!running)
{
listener.Start();
running = true;
lock (sync)
{
while (running)
{
try
{
listener.BeginAcceptTcpClient(new AsyncCallback(Accept), listener);
Monitor.Wait(sync); // Release lock and wait for a pulse
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
}
void Accept(IAsyncResult result)
{
// Let the server continue listening
lock (sync)
{
Monitor.Pulse(sync);
}
if (running)
{
TcpListener listener = (TcpListener)result.AsyncState;
using (TcpClient client = listener.EndAcceptTcpClient(result))
{
handler.Handle(client.GetStream());
}
}
}
}
そして、これが私のクライアントコードです:
class Client
{
class EchoHandler : IHandler
{
public void Handle(Stream stream)
{
System.Console.Out.Write("Echo Handler: ");
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[1024];
int count = 0;
while ((count = stream.Read(buffer, 0, 1024)) > 0)
{
sb.Append(Encoding.ASCII.GetString(buffer, 0, count));
}
System.Console.Out.WriteLine(sb.ToString());
System.Console.Out.Flush();
}
}
static IPAddress localhost = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
public static int Main()
{
Server server1 = new Server(new EchoHandler(), 1000);
Server server2 = new Server(new EchoHandler(), 1001);
server1.Start();
server2.Start();
Console.WriteLine("Press return to test...");
Console.ReadLine();
// Note interleaved ports
SendMsg("Test1", 1000);
SendMsg("Test2", 1001);
SendMsg("Test3", 1000);
SendMsg("Test4", 1001);
SendMsg("Test5", 1000);
SendMsg("Test6", 1001);
SendMsg("Test7", 1000);
Console.WriteLine("Press return to terminate...");
Console.ReadLine();
server1.Stop();
server2.Stop();
return 0;
}
public static void SendMsg(String msg, int port)
{
IPEndPoint endPoint = new IPEndPoint(localhost, port);
byte[] buffer = Encoding.ASCII.GetBytes(msg);
using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
s.Connect(endPoint);
s.Send(buffer);
}
}
}
クライアントは7つのメッセージを送信しますが、サーバーは4つしか出力しません。
Returnキーを押してテストします... Returnキーを押して終了します... エコーハンドラー:Test1 エコーハンドラー:Test3 エコーハンドラー:Test2 エコーハンドラー:Test4
が呼び出されるまでオブジェクトをロックしているはずなのに、(サーバーのメソッドで)発生する前に(Pulse
サーバーのメソッドで)発生することを許可することでモニターが混乱しているのではないかと思います。そうすれば、メソッドはロックを取得できます。そしてそのを送信します。サーバーのメソッドでこれらの2行をコメントアウトする場合:Accept
Wait
ThreadStart
ThreadStart
sync
Monitor.Wait()
Accept
Pulse
Stop()
//listener.Stop();
//running = false;
残りのメッセージは、サーバーのStop()
メソッドが呼び出されたときに表示されます(つまり、サーバーのオブジェクトをウェイクアップするsync
と、残りの着信メッセージがディスパッチされます)。ThreadStart
これはとメソッド間の競合状態でのみ発生するように思われますが、オブジェクトAccept
の周囲をロックすることでこれを防ぐことができます。sync
何か案は?
サイモン、どうもありがとう。
ps。出力が乱れているなどのことを認識していることに注意してください。特に、ロックとモニターの間の競合状態について質問しています。乾杯、SH。