【问题标题】:What is a good way to shutdown Threads blocked on NamedPipeServer#WaitForConnection?关闭 NamedPipeServer#WaitForConnection 上阻塞的线程的好方法是什么?
【发布时间】:2010-10-11 02:35:55
【问题描述】:

我启动了我的应用程序,它产生了许多线程,每个线程都创建一个 NamedPipeServer(.net 3.5 为命名管道 IPC 添加了托管类型)并等待客户端连接(块)。代码按预期运行。

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

现在我还需要一个 Shutdown 方法来彻底关闭这个过程。我尝试了通常的布尔标志 isShutdownRequested 技巧。但是管道流在 WaitForConnection() 调用上保持阻塞,并且线程不会死亡。

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

加入永远不会返回。

我没有尝试但可能可行的一个选项是调用 Thread.Abort 并吃掉异常。但这感觉不对..任何建议

2009 年 12 月 22 日更新
很抱歉没有早点发布。这是我收到的 Kim Hamilton(BCL 团队)的回复

做可中断的“正确”方式 WaitForConnection 是调用 BeginWaitForConnection,处理新的 回调中的连接,然后关闭 停止等待的管道流 连接。如果管道关闭, EndWaitForConnection 将抛出 ObjectDisposedException 其中 回调线程可以捕获、清理 任何松散的末端,然后干净地退出。

我们意识到这一定是常见的 问题,所以我团队中的某个人是 计划尽快写博客。

【问题讨论】:

    标签: c# .net-3.5 named-pipes


    【解决方案1】:

    这很俗气,但这是我工作的唯一方法。创建一个“假”客户端并连接到您的命名管道以通过 WaitForConnection。每次都有效。

    另外,即使 Thread.Abort() 也没有为我解决这个问题。


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

    【讨论】:

    • 无需重写同步工作代码就可以很好地工作;-)) 谢谢
    • 这是一个非常简单的解决方案...非常适合我。
    • 俗气,检查。工作,检查。在使用中应该有错误捕获该连接调用。
    【解决方案2】:

    切换到异步版本:BeginWaitForConnection

    如果它确实完成了,您将需要一个标志,以便完成处理程序可以调用 EndWaitForConnection 吸收任何异常并退出(调用 End... 以确保能够清理任何资源)。

    【讨论】:

    • 如果您想使用此同步样式,在调用 Begin 函数后,您可以使用 WaitHandle.WaitAny 等待 IAsyncResult.AsyncWaitHandle 以及您自己的“取消”ManualResetEvent。然后在关机时设置你的取消事件,你的线程将被解除阻塞。
    【解决方案3】:

    您可以使用以下扩展方法。请注意包含“ManualResetEvent cancelEvent” - 您可以从另一个线程设置此事件以表示等待连接方法现在应该中止并关闭管道。设置 m_bShutdownRequested 时包含 cancelEvent.Set(),关机应该比较优雅。

        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
        }
    

    【讨论】:

    • 您可以通过在没有回调的情况下调用 BeginWaitForConnection(使用 null 代替)然后在返回结果的 AsyncWaitHandle 和 cancelEvent 上使用 WaitHandle.WaitAny 来简化此操作。
    【解决方案4】:

    我写了这个扩展方法来解决这个问题:

    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
    }
    

    【讨论】:

    • 你的方法要达到什么目的?目前,它只是使用异步对应物重新实现同步WaitForConnection
    • 如果你调用stream.Dispose(),当WaitForConnection阻塞一些线程——什么都不会发生,线程会被阻塞。如果调用stream.Dispose(),当某个线程执行我的扩展方法时——WaitForConnectionEx 会抛出异常。
    【解决方案5】:

    最简单的解决方案是创建一个虚拟客户端并与服务器建立连接。

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

    【讨论】:

      【解决方案6】:

      一种可行的方法是在 WaitForConnection 之后立即检查 m_bShutdownRequested。

      在关机过程中设置布尔值。之后向所有现有管道发送虚拟消息,以便它们打开连接并检查布尔值并干净地关闭。

      【讨论】:

        【解决方案7】:
        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();
            }
        

        //用法,将 ToggleButtons 放在 StackPanel 中,然后在代码中支持它们:

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

        //对我来说就像一个魅力。尊敬的,zzzbc }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-02-20
          • 2012-06-13
          • 1970-01-01
          • 2020-05-10
          • 2019-02-27
          • 1970-01-01
          相关资源
          最近更新 更多