Qual è un buon modo per arrestare i thread bloccati su NamedPipeServer # WaitForConnection?

Comincio la mia applicazione che genera un numero di thread, ognuno dei quali crea un NamedPipeServer (.net 3.5 ha aggiunto i tipi gestiti per Named Pipe IPC) e aspetta che i client si connettano (Blocchi). Il codice funziona come previsto.

private void StartNamedPipeServer() { using (NamedPipeServerStream pipeStream = new NamedPipeServerStream(m_sPipeName, PipeDirection.InOut, m_iMaxInstancesToCreate, PipeTransmissionMode.Message, PipeOptions.None)) { m_pipeServers.Add(pipeStream); while (!m_bShutdownRequested) { pipeStream.WaitForConnection(); Console.WriteLine("Client connection received by {0}", Thread.CurrentThread.Name); .... 

Ora ho anche bisogno di un metodo Shutdown per portare questo processo in modo pulito. Ho provato la solita bool flag isShutdownRequested trick. Ma il pipestream rimane bloccato sulla chiamata WaitForConnection () e il thread non muore.

 public void Stop() { m_bShutdownRequested = true; for (int i = 0; i < m_iMaxInstancesToCreate; i++) { Thread t = m_serverThreads[i]; NamedPipeServerStream pipeStream = m_pipeServers[i]; if (pipeStream != null) { if (pipeStream.IsConnected) pipeStream.Disconnect(); pipeStream.Close(); pipeStream.Dispose(); } Console.Write("Shutting down {0} ...", t.Name); t.Join(); Console.WriteLine(" done!"); } } 

Join non ritorna mai.

Un’opzione che non ho provato ma che potrebbe funzionare è chiamare Thread.Abort e mangiare l’eccezione. Ma non sembra giusto .. Qualsiasi suggerimento

Aggiornamento 2009-12-22
Ci scusiamo per non aver postato questo prima .. Questo è quello che ho ricevuto come risposta da Kim Hamilton (team BCL)

    Il metodo “giusto” per fare un WaitForConnection interrompibile è chiamare BeginWaitForConnection, gestire la nuova connessione nel callback e chiudere il stream di pipe per interrompere l’attesa delle connessioni. Se la pipe viene chiusa, EndWaitForConnection genererà ObjectDisposedException che il thread di callback può catturare, ripulire eventuali estremità libere e uscire in modo pulito.

    Ci rendiamo conto che questa deve essere una domanda comune, quindi qualcuno nel mio team ha in programma di scrivere un blog al più presto.

    Passare alla versione asincrona: BeginWaitForConnection .

    Se mai completato, avrai bisogno di un flag in modo che il gestore di completamento possa semplicemente chiamare EndWaitForConnection assorbendo eventuali eccezioni e terminando (chiama End … per garantire che tutte le risorse siano in grado di essere ripulite).

    Questo è scadente, ma è l’unico metodo con cui ho lavorato. Crea un client ‘falso’ e connettiti alla tua named pipe per spostarti oltre WaitForConnection. Funziona sempre.

    Inoltre, anche Thread.Abort () non ha risolto il problema per me.


     _pipeserver.Dispose(); _pipeserver = null; using (NamedPipeClientStream npcs = new NamedPipeClientStream("pipename")) { npcs.Connect(100); } 

    È ansible utilizzare il seguente metodo di estensione. Notare l’inclusione di “ManualResetEvent cancelEvent”: è ansible impostare questo evento da un altro thread per segnalare che il metodo di connessione in attesa deve interrompere e chiudere la pipe. Includere cancelEvent.Set () quando si imposta m_bShutdownRequested e l’arresto dovrebbe essere relativamente aggraziato.

      public static void WaitForConnectionEx(this NamedPipeServerStream stream, ManualResetEvent cancelEvent) { Exception e = null; AutoResetEvent connectEvent = new AutoResetEvent(false); stream.BeginWaitForConnection(ar => { try { stream.EndWaitForConnection(ar); } catch (Exception er) { e = er; } connectEvent.Set(); }, null); if (WaitHandle.WaitAny(new WaitHandle[] { connectEvent, cancelEvent }) == 1) stream.Close(); if (e != null) throw e; // rethrow exception } 

    Ho scritto questo metodo di estensione per risolvere questo problema:

     public static void WaitForConnectionEx(this NamedPipeServerStream stream) { var evt = new AutoResetEvent(false); Exception e = null; stream.BeginWaitForConnection(ar => { try { stream.EndWaitForConnection(ar); } catch (Exception er) { e = er; } evt.Set(); }, null); evt.WaitOne(); if (e != null) throw e; // rethrow exception } 

    Un modo che potrebbe funzionare è il controllo di m_bShutdownRequested subito dopo WaitForConnection.

    Durante il processo di spegnimento, imposta il bool. Successivamente, invia messaggi fittizi a tutte le pipe esistenti, in modo che aprano la connessione e controllino il bool e si spengano in modo pulito.

    Una soluzione semplice e semplice è la creazione di un client fittizio e una connessione con il server.

     NamedPipeServerStream pServer; bool exit_flg=false; public void PipeServerWaiter() { NamedPipeServerStream pipeServer = new NamedPipeServerStream("DphPipe", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances); pServer = pipeServer; pipeServer.WaitForConnection(); if (exit_flg) return; thread = new Thread(PipeServerWaiter); thread.Start(); } public void Dispose() { try { exit_flg = true; NamedPipeClientStream clt = new NamedPipeClientStream(".", "DphPipe"); clt.Connect(); clt.Close(); pServer.Close(); pServer.Dispose(); } 
     using System; using System.Collections.Generic; using System.IO; using System.IO.Pipes; using System.Threading; using System.Windows; using System.Windows.Controls; namespace PIPESERVER { public partial class PWIN : UserControl { public string msg = "", cmd = "", text = ""; public NamedPipeServerStream pipe; public NamedPipeClientStream dummyclient; public string PipeName = "PIPE1"; public static string status = ""; private static int numThreads = 2; int threadId; int i; string[] word; char[] buffer; public StreamString ss; public bool ConnectDummyClient() { new Thread(() => { dummyclient = new NamedPipeClientStream(".", "PIPE1"); try { dummyclient.Connect(5000); // 5 second timeout } catch (Exception e) { Act.m.md.AMsg(e.Message); // Display error msg Act.m.console.PipeButton.IsChecked = false; } }).Start(); return true; } public bool RaisePipe() { TextBlock tb = Act.m.tb; try { pipe = new NamedPipeServerStream("PIPE1", PipeDirection.InOut, numThreads); threadId = Thread.CurrentThread.ManagedThreadId; pipe.WaitForConnection(); Act.m.md.Msg("Pipe Raised"); return true; } catch (Exception e) { string err = e.Message; tb.Inlines.Add(new Run("Pipe Failed to Init on Server Side")); tb.Inlines.Add(new LineBreak()); return false; } } public void ServerWaitForMessages() { new Thread(() => { cmd = ""; ss = new StreamString(pipe); while (cmd != "CLOSE") { try { buffer = new char[256]; text = ""; msg = ss.ReadString().ToUpper(); word = msg.Split(' '); cmd = word[0].ToUpper(); for (i = 1; i < word.Length; i++) text += word[i] + " "; switch (cmd) { case "AUTHENTICATE": ss.WriteString("I am PIPE1 server"); break; case "SOMEPIPEREQUEST":ss.WriteString(doSomePipeRequestReturningString()):break; case "CLOSE": ss.WriteString("CLOSE");// reply to client Thread.Sleep(1000);// wait for client to pick-up shutdown message pipe.Close(); Act.m.md.Msg("Server Shutdown ok"); // Server side message break; } } catch (IOException iox) { string error = iox.Message; Act.m.md.Msg(error); break; } } }).Start(); } public void DummyClientCloseServerRequest() { StreamString ss = new StreamString(dummyclient); ss.WriteString("CLOSE"); ss.ReadString(); } 

    // Usage, posiziona ToggleButtons all'interno di StackPanel, quindi esegui il back in codice così:

     private void PipeButton_Checked(object sender, RoutedEventArgs e) { Act.m.pwin.ConnectDummyClient(); Act.m.pwin.RaisePipe(); } private void PipeButton_Unchecked(object sender, RoutedEventArgs e) { Act.m.pwin.DummyClientCloseServerRequest(); Act.m.console.WaitButton.IsChecked = false; Keyboard.Focus(Act.m.md.tb1); } private void WaitButton_Checked(object sender, RoutedEventArgs e) { Act.m.pwin.Wait(); } private void WaitButton_Unchecked(object sender, RoutedEventArgs e) { } 

    //Ha funzionato benissimo per me. Rispettosamente, zzzbc}