Проблема в том, что вы используете Pulse / Wait в качестве сигнала. Правильный сигнал, такой как AutoResetEvent, имеет состояние, при котором он остается сигнальным, пока поток не вызовет WaitOne (). Вызов Pulse без каких-либо ожидающих потоков станет пустяком.
Это сочетается с тем, что один и тот же поток может многократно блокировать блокировку. Поскольку вы используете асинхронное программирование, обратный вызов Accept может быть вызван тем же потоком, что и BeginAcceptTcpClient.
Позвольте мне проиллюстрировать. Я закомментировал второй сервер и изменил код на вашем сервере.
void ThreadStart()
{
if (!running)
{
listener.Start();
running = true;
lock (sync)
{
while (running)
{
try
{
Console.WriteLine("BeginAccept [{0}]",
Thread.CurrentThread.ManagedThreadId);
listener.BeginAcceptTcpClient(new AsyncCallback(Accept), listener);
Console.WriteLine("Wait [{0}]",
Thread.CurrentThread.ManagedThreadId);
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)
{
Console.WriteLine("Pulse [{0}]",
Thread.CurrentThread.ManagedThreadId);
Monitor.Pulse(sync);
}
if (running)
{
TcpListener localListener = (TcpListener)result.AsyncState;
using (TcpClient client = localListener.EndAcceptTcpClient(result))
{
handler.Handle(client.GetStream());
}
}
}
Результат моего бега показан ниже. Если вы запустите этот код самостоятельно, значения будут отличаться, но в целом он будет таким же.
Press return to test...
BeginAccept [3]
Wait [3]
Press return to terminate...
Pulse [5]
BeginAccept [3]
Pulse [3]
Echo Handler: Test1
Echo Handler: Test3
Wait [3]
Как вы можете видеть, есть два вызванных импульса, один из отдельного потока (импульс [5]), который пробуждает первое ожидание. Затем поток 3 выполняет другой BeginAccept, но, имея ожидающие входящие соединения, этот поток решает немедленно вызвать обратный вызов Accept. Поскольку Accept вызывается тем же потоком, Lock (синхронизация) не блокируется, но Pulse [3] немедленно в пустой очереди потока.
Вызываются два обработчика и обрабатывают два сообщения.
Все в порядке, и ThreadStart снова начинает работать и переходит в состояние ожидания на неопределенное время.
Теперь основная проблема заключается в том, что вы пытаетесь использовать монитор в качестве сигнала. Поскольку он не запоминает состояние, второй импульс получает потерян.
Но для этого есть простое решение. Используйте AutoResetEvents, который является правильным сигналом, и он запомнит свое состояние.
public Server(IHandler handler, int port)
{
this.handler = handler;
IPAddress address = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0];
listener = new TcpListener(address, port);
running = false;
_event = new AutoResetEvent(false);
}
public void Start()
{
Thread thread = new Thread(ThreadStart);
thread.Start();
}
public void Stop()
{
listener.Stop();
running = false;
_event.Set();
}
void ThreadStart()
{
if (!running)
{
listener.Start();
running = true;
while (running)
{
try
{
listener.BeginAcceptTcpClient(new AsyncCallback(Accept), listener);
_event.WaitOne();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
void Accept(IAsyncResult result)
{
// Let the server continue listening
_event.Set();
if (running)
{
TcpListener localListener = (TcpListener) result.AsyncState;
using (TcpClient client = localListener.EndAcceptTcpClient(result))
{
handler.Handle(client.GetStream());
}
}
}