C# 关闭在 NamedPipeServer#WaitForConnection 上阻塞的线程的好方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/607872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is a good way to shutdown Threads blocked on NamedPipeServer#WaitForConnection?
提问by Gishu
I start my application which spawns a number of Threads, each of which creates a NamedPipeServer (.net 3.5 added managed types for Named Pipe IPC) and waits for clients to connect (Blocks). The code functions as intended.
我启动了我的应用程序,它产生了许多线程,每个线程创建一个 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);
....
Now I also need a Shutdown method to bring this process down cleanly. I tried the usual bool flag isShutdownRequested trick. But the pipestream stays blocked on the WaitForConnection() call and the thread doesn't die.
现在我还需要一个 Shutdown 方法来彻底关闭这个过程。我尝试了通常的 bool 标志 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!");
}
}
Join never returns.
加入永不返回。
An option that I didnt try but would possibly work is to call Thread.Abort and eat up the exception. But it doesn't feel right.. Any suggestions
我没有尝试但可能有效的一个选项是调用 Thread.Abort 并处理异常。但是感觉不对..有什么建议
Update 2009-12-22
Sorry for not posting this earlier.. This is what I received as a response from Kim Hamilton (BCL team)
2009 年 12 月 22 日更新,
抱歉没有早点发布。这是我收到 Kim Hamilton(BCL 团队)的回复
The "right" way to do an interruptible WaitForConnection is to call BeginWaitForConnection, handle the new connection in the callback, and close the pipe stream to stop waiting for connections. If the pipe is closed, EndWaitForConnection will throw ObjectDisposedException which the callback thread can catch, clean up any loose ends, and exit cleanly.
We realize this must be a common question, so someone on my team is planning to blog about this soon.
执行可中断 WaitForConnection 的“正确”方法是调用 BeginWaitForConnection,在回调中处理新连接,并关闭管道流以停止等待连接。如果管道关闭,EndWaitForConnection 将抛出 ObjectDisposedException 回调线程可以捕获的异常,清理任何松散的末端,并干净地退出。
我们意识到这一定是一个常见问题,因此我团队中的某个人计划很快就此事发表博客。
采纳答案by Richard
Switch to the asynchronous version: BeginWaitForConnection
.
切换到异步版本:BeginWaitForConnection
.
If it does ever complete, you'll need a flag so the completion handler can just call EndWaitForConnection
absorbing any exceptions and exiting (call End... to ensure any resources are able to be cleaned up).
如果它确实完成了,您将需要一个标志,以便完成处理程序可以调用EndWaitForConnection
吸收任何异常并退出(调用 End... 以确保能够清理任何资源)。
回答by Sam Saffron
One way that could work is checking for m_bShutdownRequested right after the WaitForConnection.
一种可行的方法是在 WaitForConnection 之后立即检查 m_bShutdownRequested。
During the shutdown process set the bool. After that send dummy messages to all the existing pipes so they open the connection and check the bool and shut down cleanly.
在关闭过程中设置布尔值。之后向所有现有管道发送虚拟消息,以便它们打开连接并检查 bool 并干净地关闭。
回答by JasonRShaver
This is cheesy, but it is the only method I have gotten to work. Create a 'fake' client and connect to your named pipe to move past the WaitForConnection. Works every time.
这很俗气,但这是我开始工作的唯一方法。创建一个“假”客户端并连接到您的命名管道以移过 WaitForConnection。每次都有效。
Also, even Thread.Abort() did not fix this issue for me.
另外,即使是 Thread.Abort() 也没有为我解决这个问题。
_pipeserver.Dispose();
_pipeserver = null;
using (NamedPipeClientStream npcs = new NamedPipeClientStream("pipename"))
{
npcs.Connect(100);
}
回答by Evgeny Lazin
I wrote this extension method to solve this problem:
我写了这个扩展方法来解决这个问题:
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
}
回答by Deirh
You can use the following extension method. Note the inclusion of 'ManualResetEvent cancelEvent' - you can set this event from another thread to signal that the waiting connect method should abort now and close the pipe. Include cancelEvent.Set() when setting m_bShutdownRequested and the shutdown should be relatively graceful.
您可以使用以下扩展方法。请注意包含“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
}
回答by user3256430
An simplest and easy solution is creating a dummy client and do a connection with the server.
一个最简单的解决方案是创建一个虚拟客户端并与服务器建立连接。
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();
}
回答by zzzbc
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();
}
//Usage, Place ToggleButtons inside StackPanel, and back them in code thus:
// 用法,将 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)
{
}
//Worked like a charm for me. Respectfully, zzzbc }
//对我来说就像一个魅力。恭敬的,zzzbc }