Quel est un bon moyen d'arrêter les threads bloqués sur NamedPipeServer # WaitForConnection?

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

  •  03-07-2019
  •  | 
  •  

Question

Je lance mon application qui génère un certain nombre de threads, chacun créant un serveur NamedPipeServer (types gérés ajoutés par .net 3.5 pour Named Pipe IPC) et attend que les clients se connectent (Blocs). Le code fonctionne comme prévu.

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);
        ....  

Maintenant, j’ai également besoin d’une méthode Shutdown pour mettre ce processus en marche proprement. J'ai essayé le drapeau bool habituel, l'astuce isShutdownRequested. Mais le pipeline reste bloqué sur l'appel WaitForConnection () et le thread ne meurt pas.

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

La jointure ne revient jamais.

Une option que je n’ai pas essayée mais qui pourrait fonctionner est d’appeler Thread.Abort et de manger l’exception. Mais ça ne se sent pas bien. Des suggestions

Mise à jour 2009-12-22
Désolé de ne pas avoir posté ceci plus tôt. C’est ce que j’ai reçu en réponse de Kim Hamilton (équipe de la BCL)

  

Le " right " façon de faire un interruptible   WaitForConnection est d'appeler   BeginWaitForConnection, gérez le nouveau   connexion dans le rappel, et fermez   le cours d'eau à arrêter d'attendre   les liaisons. Si le tuyau est fermé,   EndWaitForConnection lancera   ObjectDisposedException dont le   le fil de rappel peut attraper, nettoyer   toute partie lâche et sortez proprement.

     

Nous réalisons que cela doit être commun   question, donc quelqu'un de mon équipe est   l'intention de bloguer à ce sujet bientôt.

Était-ce utile?

La solution

Passez à la version asynchrone: BeginWaitForConnection .

Si cela se termine jamais, vous aurez besoin d'un indicateur pour que le gestionnaire d'achèvement puisse simplement appeler EndWaitForConnection en absorbant les exceptions et en quittant (appelez End ... pour vous assurer que toutes les ressources peuvent être nettoyées up).

Autres conseils

C’est ringard, mais c’est la seule méthode que j’ai appris à utiliser. Créez un «faux» client et connectez-vous à votre canal nommé pour passer au-delà de WaitForConnection. Fonctionne à chaque fois.

De même, même Thread.Abort () n'a pas résolu ce problème pour moi.

_pipeserver.Dispose();
_pipeserver = null;

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

Vous pouvez utiliser la méthode d'extension suivante. Notez l'inclusion de 'ManualResetEvent cancelEvent' - vous pouvez définir cet événement à partir d'un autre thread pour signaler que la méthode de connexion en attente doit abandonner maintenant et fermer le canal. Incluez cancelEvent.Set () lors du paramétrage de m_bShutdownRequested et si l'arrêt doit être relativement fluide.

    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
    }

J'ai écrit cette méthode d'extension pour résoudre ce problème:

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
}

Une méthode qui pourrait fonctionner consiste à rechercher m_bShutdownRequested juste après WaitForConnection.

Pendant le processus d'arrêt, définissez la valeur booléenne. Après cela, envoyez des messages fictifs à tous les tuyaux existants pour qu'ils ouvrent la connexion, vérifient le booléen et s'arrêtent proprement.

Une solution simple et facile consiste à créer un client factice et à établir une connexion avec le serveur.

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();
    }

// Utilisation, placez les boutons bascules dans StackPanel et sauvegardez-les dans le code comme suit:

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

// A travaillé comme un charme pour moi. Respectueusement, zzzbc     }

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top