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.

0voto

John Leidegren Points 21951

Vous devez fermer() le tuyau, cela interrompra toutes les opérations de blocage associées au tuyau.

Si vous n'avez pas de référence au tuyau, vous devrez revoir la conception de votre application pour signaler que vous voulez fermer ce tuyau.

0voto

Sam Saffron Points 56236

Une façon qui pourrait fonctionner est de vérifier m_bShutdownRequested juste après le WaitForConnection.

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

-1voto

zzzbc Points 1
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 ToggleButtons à l'intérieur du StackPanel, et retournez-les dans le code ainsi :

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 a marché comme un charme pour moi. Respectueusement, zzzbc }

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