44 votes

Quelle est la bonne façon d'arrêter les threads bloqués sur NamedPipeServer#WaitForConnection ?

Je lance mon application qui génère un certain nombre de threads, chacun d'entre eux créant un NamedPipeServer (.net 3.5 a ajouté des types gérés pour Named Pipe IPC) et attendant 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 aussi besoin d'une méthode d'arrêt pour arrêter ce processus proprement. J'ai essayé le truc habituel du bool flag isShutdownRequested. Mais le pipestream 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!");
   }
} 

Join 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 cela ne me semble pas correct. Des suggestions ?

Mise à jour 2009-12-22
Désolé de ne pas avoir posté ceci plus tôt Voici ce que j'ai reçu comme réponse de Kim Hamilton (équipe BCL)

La "bonne" façon de faire un interruptible WaitForConnection interruptible est d'appeler BeginWaitForConnection, de gérer le nouvel événement nouvelle connexion dans le callback, et de fermer le flux de tuyaux pour arrêter d'attendre que connexions. Si le tuyau est fermé, EndWaitForConnection lèvera l'exception ObjectDisposedException que le fil de rappel peut attraper, nettoyer et quitter proprement.

Nous réalisons que cela doit être un problème commun question, donc quelqu'un de mon équipe prévoit d'ouvrir un blog sur ce sujet prochainement.

48voto

JasonRShaver Points 2651

C'est ringard, mais c'est la seule méthode que j'ai réussi à faire fonctionner. Créez un 'faux' client et connectez-vous à votre pipe nommé pour passer le WaitForConnection. Cela fonctionne à chaque fois.

De plus, 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);
}

18voto

Richard Points 54016

Passez à la version asynchrone : BeginWaitForConnection .

Si elle se termine, vous aurez besoin d'un drapeau pour que le gestionnaire d'achèvement puisse juste appeler EndWaitForConnection absorbe toutes les exceptions et se termine (appelez End... pour vous assurer que toutes les ressources peuvent être nettoyées).

8voto

Deirh Points 46

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 connect en attente doit abandonner maintenant et fermer le tube. Incluez cancelEvent.Set() lors de la définition de m_bShutdownRequested et la fermeture devrait être relativement gracieuse.

    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
    }

1voto

Lazin Points 4481

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
}

1voto

user3256430 Points 11

La solution la plus simple et la plus facile consiste à créer un client fictif 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();

    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X