다중 스레드 TCP 서버에서 Monitor.Wait 및 Monitor.Pulse와 인터록 된 문제가 있습니다. 내 문제를 설명하기 위해, 여기 내 서버의 코드입니다 : 여기다중 스레드 서버에서 Monitor.Wait/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);
}
}
}
클라이언트는 일곱 메시지를 보내지 만, 서버는 네 개의 인쇄 :
Press return to test... Press return to terminate... Echo Handler: Test1 Echo Handler: Test3 Echo Handler: Test2 Echo Handler: Test4
Wait
이 발생하기 전에 (서버의 Accept
방법으로) Pulse
이 발생하여 모니터가 혼란 스럽습니다. (i n ThreadStart
메서드), Monitor.Wait()
을 호출하고 Accept
메서드를 호출하여 Pulse
을 보낼 때까지 ThreadStart
이 sync
개체에 대한 잠금을 유지해야하는 경우에도 마찬가지입니다. 서버의 Stop()
방법이 두 줄을 주석 경우 서버의 Stop()
메서드를 호출 할 때
//listener.Stop();
//running = false;
나머지 메시지가 표시 (즉 서버의 sync
객체를 깨어하면 나머지 수신 메시지를 발송됩니다). 이것은 내게는 ThreadStart
과 Accept
메서드 사이의 경쟁 조건에서만 발생할 수 있지만 sync
개체의 잠금은이를 방지해야합니다.
아이디어가 있으십니까?
감사합니다. Simon.
ps. 출력이 순서가 맞지 않는 것처럼 보임에 유의하십시오. 잠금과 모니터 사이의 경쟁 조건에 대해 구체적으로 묻습니다. 건배, SH.
Thanks Mats. BeginAcceptTcpClient는 항상 별도의 스레드에서 실행되므로 동기화 객체를 중요한 섹션으로 사용할 수 있다고 가정했습니다. 당신은 자리에 있었고 신호는 갈 길입니다. 다시 한번 감사드립니다. SH –