Sto riscontrando un problema con monitor bloccato. Attesa e monitoraggio.Pulsare in un server TCP multi-thread. Per dimostrare i miei problemi, ecco il mio codice del server:Monitor. Condizione corsa/impulso in un server con multithreading
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());
}
}
}
}
E qui è il mio codice cliente:
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);
}
}
}
il client invia sette messaggi, ma il server stampa solo quattro:
Press return to test... Press return to terminate... Echo Handler: Test1 Echo Handler: Test3 Echo Handler: Test2 Echo Handler: Test4
Ho il sospetto che il monitor si confonda consentendo che si verifichi Pulse
(nel metodo Accept
del server) prima che si verifichi lo Wait
(i n il metodo ThreadStart
, anche se lo ThreadStart
deve ancora avere il blocco sull'oggetto sync
finché non chiama Monitor.Wait()
e quindi il metodo Accept
può acquisire il blocco e inviare il suo Pulse
. Se di commentare queste due linee nel metodo Stop()
del server:
//listener.Stop();
//running = false;
I messaggi restanti vengono visualizzati quando il metodo Stop()
del server si chiama (vale a dire il risveglio sync
oggetto del server induce a spedire i messaggi in arrivo rimanenti). Mi sembra che questo si possa verificare solo in una condizione di competizione tra i metodi e Accept
, ma il blocco attorno all'oggetto sync
dovrebbe impedirlo.
Qualche idea?
Molte grazie, Simon.
ps. Si noti che sono consapevole che l'output appare fuori ordine ecc., Sto specificatamente chiedendo una condizione di competizione tra serrature e monitor. Saluti, SH.
Grazie Mats. Supponevo che BeginAcceptTcpClient funzionasse sempre su un thread separato e quindi potevo usare l'oggetto sync come una sezione critica. Tu eri azzeccato e i segnali sono la strada da percorrere. Grazie ancora. SH –