¿Cuál es una buena manera de cerrar los subprocesos bloqueados en NamedPipeServer # WaitForConnection?

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

  •  03-07-2019
  •  | 
  •  

Pregunta

Comienzo mi aplicación que genera una serie de subprocesos, cada uno de los cuales crea un NamedPipeServer (.net 3.5 agregados tipos administrados para Named Pipe IPC) y espera que los clientes se conecten (Bloques). El código funciona según lo 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);
        ....  

Ahora también necesito un método de apagado para desactivar este proceso de manera limpia. Probé el truco habitual del indicador de bool isShutdownRequested. Pero el flujo de flujo permanece bloqueado en la llamada WaitForConnection () y el hilo no se muere.

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!");
   }
} 

Unirse nunca vuelve.

Una opción que no probé pero que posiblemente funcionaría es llamar a Thread.Abort y devorar la excepción. Pero no se siente bien ... Cualquier sugerencia

Actualización 2009-12-22
Lo siento por no haber publicado esto antes. Esto es lo que recibí como respuesta de Kim Hamilton (equipo de BCL)

  

El " derecha " forma de hacer un interrumpible   WaitForConnection es llamar   BeginWaitForConnection, manejar el nuevo   Conexión en el callback, y cierre.   la corriente de la tubería para dejar de esperar   conexiones Si el tubo está cerrado,   EndWaitForConnection lanzará   ObjectDisposedException que el   hilo de devolución de llamada puede atrapar, limpiar   cualquier cabos sueltos, y salga limpiamente.

     

Nos damos cuenta de que esto debe ser un común   pregunta, entonces alguien en mi equipo es   planeando hacer un blog sobre esto pronto.

¿Fue útil?

Solución

Cambie a la versión asíncrona: BeginWaitForConnection .

Si alguna vez se completa, necesitará un indicador para que el controlador de finalización pueda simplemente llamar a EndWaitForConnection para absorber cualquier excepción y salir (llame a End ... para asegurarse de que se puedan limpiar los recursos) arriba).

Otros consejos

Esto es cursi, pero es el único método que he llegado a trabajar. Cree un cliente "falso" y conéctese a su canalización con nombre para moverse más allá de WaitForConnection. Funciona cada vez.

Además, incluso Thread.Abort () no solucionó este problema para mí.


_pipeserver.Dispose();
_pipeserver = null;

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

Puedes usar el siguiente método de extensión. Tenga en cuenta la inclusión de 'ManualResetEvent cancelEvent': puede configurar este evento desde otro hilo para indicar que el método de conexión en espera debe abortarse ahora y cerrar la canalización. Incluya cancelEvent.Set () al configurar m_bShutdownRequested y el cierre debe ser relativamente elegante.

    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
    }

Escribí este método de extensión para resolver este 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
}

Una forma en que podría funcionar es buscar m_bShutdownRequested justo después de WaitForConnection.

Durante el proceso de cierre, configure el bool. Después de eso, envíe mensajes falsos a todas las tuberías existentes para que abran la conexión y verifiquen el bool y se apaguen limpiamente.

Una solución más simple y sencilla es crear un cliente ficticio y hacer una conexión con el servidor.

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, coloque ToggleButtons dentro de StackPanel, y haga una copia de seguridad en el código de esta manera:

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)
    {
    }

// Trabajó como un encanto para mí. Respetuosamente, zzzbc     }

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top