StreamReader.Peek和Thread.Interrupt的替代方案

我正在尝试做的快速序言。 我想启动一个进程并启动两个线程来监视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阻塞相关。

添加了有关此服务器的信息。

此外,我开始意识到更好的查询排队方法可能是最终的解决方案。

我可以说这整个混乱源于Peek阻止的事实。 你真的试图修复一些在框架中根本被破坏的东西,这从来都不容易(即不是一个肮脏的黑客)。 就个人而言,我会解决问题的根源,即阻塞Peek 。 Mono将遵循微软的实施,因此最终会遇到同样的问题。

虽然我知道如何解决问题,但是我应该允许更改框架源代码,但解决方法是漫长而耗时的。

但是这里。

从本质上讲,Microsoft需要做的是更改Process.StartWithCreateProcess ,以便为standardOutputstandardError分配一个特殊类型的StreamReader (例如PipeStreamReader )。

在这个PipeStreamReader ,他们需要覆盖两个ReadBuffer重载(即需要首先将两个重载更改为StreamReader虚拟),这样在读取之前, PeekNamedPipe被调用来执行实际查看。 就像现在一样,当没有数据可供读取时, FileStream.Read() (由Peek()调用)将阻塞管道读取。 虽然具有0字节的FileStream.Read()在文件上运行良好,但它在管道上并不能很好地工作。 事实上,.NET团队错过了管道文档的一个重要部分 – PeekNamedPipe WinAPI。

PeekNamedPipe函数类似于ReadFile函数,但有以下例外:

即使管道中没有数据,该函数也会立即在单线程应用程序中返回。 命名管道句柄的等待模式(阻塞或非阻塞)对该function没有影响。

此时没有在框架中解决此问题的最好的事情就是推出自己的Process类(围绕WinAPI的瘦包装就足够了)。

你为什么不把两个线程都设置为背景然后杀死应用程序? 它会导致两个线程立即关闭。

你正在构建一个服务器。 你想避免阻止。 显而易见的解决方案是使用异步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工作:)

摆脱窥视并使用下面的方法从过程输出流中读取。 ReadLine()在进程结束时返回null。 要与调用线程一起加入此线程,请等待进程结束或自行终止进程。 ShutdownClient()应该只是Kill()进程,这将导致读取StdOut或StdErr的另一个线程也退出。

  private void ReadToEnd() { string nextLine; while ((nextLine = stream.ReadLine()) != null) { output.WriteLine(nextLine); } }