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

StackOverflow https://stackoverflow.com/questions/607872

  •  03-07-2019
  •  | 
  •  

Domanda

Avvio la mia applicazione che genera un numero di thread, ognuno dei quali crea un NamedPipeServer (.net 3.5 ha aggiunto tipi gestiti per Named Pipe IPC) e attende che i client si connettano (Blocks). 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 di spegnimento per mettere in ordine questo processo. Ho provato il solito 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!");
   }
} 

L'unione non ritorna mai.

Un'opzione che non ho provato ma che potrebbe funzionare è quella di chiamare Thread.Abort e recuperare l'eccezione. Ma non sembra giusto .. Eventuali suggerimenti

Aggiornamento 22/12/2009
Ci scusiamo per non averlo pubblicato prima. Questo è quello che ho ricevuto come risposta da Kim Hamilton (team BCL)

  

Il " giusto " modo di fare un interrompibile   WaitForConnection deve chiamare   BeginWaitForConnection, gestisci il nuovo   connessione nel callback e chiusura   il flusso di pipe per smettere di aspettare   connessioni. Se il tubo è chiuso,   EndWaitForConnection genererà   ObjectDisposedException che il   il thread di callback può catturare, ripulire   eventuali estremità libere e uscire in modo pulito.

     

Ci rendiamo conto che questo deve essere un comune   domanda, quindi qualcuno nella mia squadra lo è   sto pianificando di blog presto su questo.

È stato utile?

Soluzione

Passa alla versione asincrona: BeginWaitForConnection .

Se viene mai completato, avrai bisogno di un flag in modo che il gestore del completamento possa semplicemente chiamare EndWaitForConnection assorbendo eventuali eccezioni ed uscendo (chiama End ... per garantire che tutte le risorse possano essere pulite up).

Altri suggerimenti

Questo è di cattivo gusto, ma è l'unico metodo con cui ho avuto modo di lavorare. Crea un client "falso" e connettiti alla tua pipe denominata per passare oltre WaitForConnection. Funziona ogni volta.

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


_pipeserver.Dispose();
_pipeserver = null;

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

È possibile utilizzare il seguente metodo di estensione. Si noti l'inclusione di 'ManualResetEvent cancelEvent': è possibile impostare questo evento da un altro thread per segnalare che il metodo di connessione in attesa dovrebbe interrompersi ora e chiudere la pipe. Includi cancelEvent.Set () quando si imposta m_bShutdownRequested e l'arresto dovrebbe essere relativamente grazioso.

    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 impostare il valore bool. Dopodiché invia messaggi fittizi a tutti i tubi esistenti in modo che aprano la connessione e controllino il bool e si chiudano in modo pulito.

Una soluzione più semplice e facile è creare un client fittizio e stabilire 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();
    }

// Uso, posiziona i pulsanti Toggle all'interno di StackPanel e esegui il backup nel codice in questo modo:

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 come un incantesimo per me. Rispettivamente, zzzbc     }

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top