我正在尝试做的快速序言。我想启动一个进程并启动两个线程来监视stderr和stdin。每个线程都会扼杀流中的一些内容,然后将其发送到NetworkStream。如果任一线程中都有错误,则两个线程都需要立即死亡。
这些具有stdout和stdin监视线程的进程中的每一个都由主服务器进程分离出来。这变得棘手的原因是因为在任何给定时间都可以容易地有40或50个这样的过程。只有在早上重新启动时才会有超过50个连接,但它确实需要能够处理100个或更多。我测试了100个同时连接。
try
{
StreamReader reader = this.myProcess.StandardOutput;
char[] buffer = new char[4096];
byte[] data;
int read;
while (reader.Peek() > -1 ) // This can block before stream is streamed to
{
read = reader.Read(buffer, 0, 4096);
data = Server.ClientEncoding.GetBytes(buffer, 0, read);
this.clientStream.Write(data, 0, data.Length); //ClientStream is a NetworkStream
}
}
catch (Exception err)
{
Utilities.ConsoleOut(string.Format("StdOut err for client {0} -- {1}", this.clientID, err));
this.ShutdownClient(true);
}
此代码块在一个Thread中运行,该Thread现在不是Background。 StandardError流有一个类似的线程。我正在使用此方法而不是侦听OutputDataReceived和ErrorDataReceived,因为Mono中存在导致这些事件不能始终正常触发的问题,即使它现在似乎已修复,我也喜欢这种方法确保我正在阅读和编写所有内容顺序。
ShutdownClient with True只是试图杀死两个线程。不幸的是,我发现使这项工作的唯一方法是在stdErrThread和stdOutThread对象上使用中断。理想情况下,peek不会阻塞,我可以使用手动重置事件继续检查stdOut或stdIn上的新数据,然后在事件被翻转时死掉。
我怀疑这是最好的方法。有没有办法在不使用中断的情况下执行此操作?
我想改变,因为我刚刚在日志中看到我错过了在Utlities.ConsoleOut中抛出的ThreadInterruptException。如果静态变量为true,这只是一个System.Console.Write,但我猜这会阻塞某个地方。
编辑:
这些线程是父线程的一部分,该线程由服务器在请求时大量启动。因此,我无法将StdOut和StdErr线程设置为后台并终止应用程序。我可以从主服务器中删除父线程,但是这会再次与Peek阻塞相关。
添加了有关此服务器的信息。
此外,我开始意识到更好的查询排队方法可能是最终的解决方案。
答案 0 :(得分:2)
我可以说这整个混乱源于Peek
阻塞的事实。你真的试图修复一些在框架中根本被破坏的东西,这从来都不容易(即不是一个肮脏的黑客攻击)。就个人而言,我会解决问题的根源,即阻塞Peek
。 Mono会跟随微软的实施,因此也会遇到同样的问题。
虽然我确切知道如何解决问题,但是我应该允许更改框架源代码,但解决方法是漫长而耗时的。
但是这里有。
基本上,Microsoft需要做的是更改Process.StartWithCreateProcess
,以便为standardOutput
和standardError
分配一个特殊类型的StreamReader
(例如PipeStreamReader
)
在这个PipeStreamReader
中,他们需要覆盖两个ReadBuffer
重载(即需要先在StreamReader
中将两个重载更改为虚拟),以便在读取之前{{1}被称为做实际的偷看。就目前而言,当没有可用于读取的数据时,PeekNamedPipe
(由FileStream.Read()
调用)将阻止管道读取。虽然具有0字节的Peek()
在文件上运行良好,但它在管道上的效果不佳。事实上,.NET团队错过了管道文档的重要部分 - PeekNamedPipe WinAPI。
PeekNamedPipe函数类似于ReadFile函数,但有以下例外:
...
该函数始终在单线程应用程序中立即返回, 即使管道中没有数据 。命名管道句柄的等待模式(阻塞或非阻塞)对函数没有影响。
此时没有在框架中解决此问题的最好的事情就是推出自己的Process类(围绕WinAPI的瘦包装就足够了)。
答案 1 :(得分:0)
为什么不将两个线程都设置为背景然后杀死应用程序?它会导致两个线程立即关闭。
答案 2 :(得分:0)
您正在构建服务器。你想避免阻止。显而易见的解决方案是使用异步API:
var myProcess = Process.GetCurrentProcess();
StreamReader reader = myProcess.StandardOutput;
char[] buffer = new char[4096];
byte[] data;
int read;
while (!myProcess.HasExited)
{
read = await reader.ReadAsync(buffer, 0, 4096);
data = Server.ClientEncoding.GetBytes(buffer, 0, read);
await this.clientStream.WriteAsync(data, 0, data.Length);
}
无需浪费线程进行I / O工作:)
答案 3 :(得分:-1)
摆脱窥视并使用下面的方法从过程输出流中读取。 ReadLine()在进程结束时返回null。要与调用线程一起加入此线程,请等待进程结束或自行终止进程。 ShutdownClient()应该只是Kill()进程,这将导致读取StdOut或StdErr的另一个线程也退出。
private void ReadToEnd()
{
string nextLine;
while ((nextLine = stream.ReadLine()) != null)
{
output.WriteLine(nextLine);
}
}