Was ist ein guter Weg, Themen zur Abschaltung blockiert auf NamedPipeServer # waitForConnection?
-
03-07-2019 - |
Frage
ich meine Anwendung starten, die eine Anzahl von Threads laicht, von denen jeder eine NamedPipeServer erstellt (.net 3.5 hinzugefügt Typen für Named Pipe IPC verwaltet) und wartet auf Kunden (Blöcke) zu verbinden. Die Code-Funktionen wie vorgesehen.
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);
....
Jetzt muß ich auch eine Shutdown Methode, um diesen Prozess zu bringen, sauber nach unten. Ich habe versucht, den üblichen bool Flag isShutdownRequested Trick. Aber die PipeStream bleibt auf dem waitForConnection () Aufruf blockiert und der Faden nicht stirbt.
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 nie zurückgibt.
Eine Option, die ich versuche, knapp aber würde möglicherweise Arbeit ist Thread.Abort zu nennen und die Ausnahme auffressen. Aber es fühlt sich nicht richtig .. Irgendwelche Vorschläge
Aktualisieren 2009-12-22
Es tut uns das nicht früher veröffentlichen .. Das ist, was ich als eine Antwort von Kim Hamilton (BCL-Team) erhalten
Der „richtigen“ Weg, um eine unterbrechbare zu tun WaitForConnection ist zu nennen BeginWaitForConnection, handhaben die neue Verbindung in dem Rückruf, und in der Nähe der Rohrstrom zu stoppen warten für Verbindungen. Wenn das Rohr geschlossen ist, EndWaitForConnection wird werfen ObjectDisposedException die der Callback-Thread kann fangen, aufzuräumen alle losen Enden und Ausfahrt sauber.
Wir erkennen dies ein weit verbreitetes sein muss Frage, so jemand in meinem Team ist Planung über diese bald bloggen.
Lösung
Wechseln Sie auf die asynchrone Version:. BeginWaitForConnection
Wenn es überhaupt abgeschlossen wird, werden Sie eine Flagge brauchen so der Handler Abschluss kann nur EndWaitForConnection
alle Ausnahmen zu absorbieren rufen und Beenden (Call End ..., um sicherzustellen, alle Ressourcen der Lage sind, bereinigt werden).
Andere Tipps
Das ist kitschig, aber es ist die einzige Methode, die ich bekommen haben zu arbeiten. Erstellen Sie ein ‚fake‘ Client und eine Verbindung zu Ihrem Named Pipe vorbei waitForConnection zu bewegen. Funktioniert jedes Mal.
Auch selbst Thread.Abort () nicht für mich beheben dieses Problem.
_pipeserver.Dispose();
_pipeserver = null;
using (NamedPipeClientStream npcs = new NamedPipeClientStream("pipename"))
{
npcs.Connect(100);
}
Sie können folgende Erweiterungsmethode verwenden. Beachten Sie die Aufnahme von ‚Manual cancelEvent‘ - dieses Ereignis von einem anderen Thread eingestellt werden, um zu signalisieren, dass die Warte connect-Methode jetzt abbrechen sollte und das Rohr schließen. Fügen Sie cancelEvent.Set (), wenn m_bShutdownRequested Einstellung und das Herunterfahren sollte relativ anmutig sein.
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
}
Ich schrieb diese Erweiterung Methode, um dieses Problem zu lösen:
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
}
Eine Möglichkeit, die für m_bShutdownRequested direkt nach dem waitForConnection prüft arbeiten konnte.
Während des Herunterfahren stellen Sie den Bool. Danach senden Dummy-Nachrichten an alle vorhandenen Rohrleitungen, so dass sie die Verbindung und überprüfen Sie die Bool und Abfahren sauber öffnen.
Eine einfache und einfache Lösung ist ein Dummy-Client erstellen und eine Verbindung mit dem Server tun.
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();
}
// Usage, Place ToggleButtons innerhalb Stackpanel, und sie im Code zurück also:
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)
{
}
// wie ein Charme für mich. Hochachtungsvoll, zzzbc }