问题描述
我正在尝试做的快速前言.我想启动一个进程并启动两个线程来监控stderr和stdin.每个线程咀嚼流的位,然后将其发射到 NetworkStream.如果任一线程出现错误,则两个线程都需要立即终止.
Quick preface of what I'm trying to do. I want to start a process and start up two threads to monitor the stderr and stdin. Each thread chews off bits of the stream and then fires it out to a NetworkStream. If there is an error in either thread, both threads need to die immediately.
每个带有 stdout 和 stdin 监控线程的进程都由一个主服务器进程分离出来.这变得棘手的原因是因为在任何给定时间很容易有 40 或 50 个这样的过程.只有在早上重启爆发期间,才会有超过 50 个连接,但它确实需要能够处理 100 个或更多.我测试了 100 个同时连接.
Each of these processes with stdout and stdin monitoring threads are spun off by a main server process. The reason this becomes tricky is because there can easily be 40 or 50 of these processes at any given time. Only during morning restart bursts are there ever more than 50 connections, but it really needs to be able to handle 100 or more. I test with 100 simultaneous connections.
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);
}
此代码块在一个线程中运行,该线程现在不是后台.StandardError 流有一个类似的线程.我正在使用此方法而不是收听 OutputDataReceived 和 ErrorDataReceived,因为 Mono 中存在一个问题,导致这些事件并不总是正确触发,即使它现在似乎已修复,我喜欢这种方法确保我正在读取和写入所有内容顺序.
This code block is run in one Thread which is right now not Background. There is a similar thread for the StandardError stream. I am using this method instead of listening to OutputDataReceived and ErrorDataReceived because there was an issue in Mono that caused these events to not always fire properly and even though it appears to be fixed now I like that this method ensures I'm reading and writing everything sequentially.
True 的 ShutdownClient 只是试图杀死两个线程.不幸的是,我发现完成这项工作的唯一方法是在 stdErrThread 和 stdOutThread 对象上使用中断.理想情况下 peek 不会阻塞,我可以使用手动重置事件来继续检查 stdOut 或 stdIn 上的新数据,然后在事件翻转时死掉.
ShutdownClient with True simply tries to kill both threads. Unfortunately the only way I have found to make this work is to use an interrupt on the stdErrThread and stdOutThread objects. Ideally peek would not block and I could just use a manual reset event to keep checking for new data on stdOut or stdIn and then just die when the event is flipped.
我怀疑这是最好的方法.有没有办法在不使用中断的情况下执行此操作?
I doubt this is the best way to do it. Is there a way to execute this without using an Interrupt?
我想更改,因为我刚刚在日志中看到我错过了在 Utlities.ConsoleOut 中抛出的 ThreadInterruptException.如果静态变量为真,这只会执行 System.Console.Write,但我猜这会在某处阻塞.
I'd like to change, because I just saw in my logs that I missed a ThreadInterruptException thrown inside Utlities.ConsoleOut. This just does a System.Console.Write if a static variable is true, but I guess this blocks somewhere.
这些线程是父线程的一部分,由服务器根据请求集体启动.因此,我无法将 StdOut 和 StdErr 线程设置为后台并终止应用程序.我可以从主服务器中终止父线程,但这又会因 Peek 阻塞而变得棘手.
These threads are part of a parent Thread that is launched en masse by a server upon a request. Therefore I cannot set the StdOut and StdErr threads to background and kill the application. I could kill the parent thread from the main server, but this again would get sticky with Peek blocking.
添加了关于这是服务器的信息.
Added info about this being a server.
我也开始意识到更好的查询队列方法可能是最终的解决方案.
Also I'm starting to realize a better Queuing method for queries might be the ultimate solution.
推荐答案
我可以说这整个混乱源于 Peek
阻塞的事实.您实际上是在尝试修复框架中从根本上被破坏的东西,而且这绝非易事(即不是肮脏的 hack).就个人而言,我会解决问题的根源,即阻塞 Peek
.Mono 会遵循 Microsoft 的实施,因此会遇到同样的问题.
I can tell this whole mess stems from the fact that Peek
blocks. You're really trying to fix something that is fundamentally broken in the framework and that is never easy (i.e. not a dirty hack). Personally, I would fix the root of the problem, which is the blocking Peek
. Mono would've followed Microsoft's implementation and thus ends up with the same problem.
虽然我确切知道如果允许我更改框架源代码如何解决该问题,但解决方法既冗长又耗时.
While I know exactly how to fix the problem should I be allowed to change the framework source code, the workaround is lengthy and time consuming.
但是这里有.
本质上,微软需要做的是改变 Process.StartWithCreateProcess
使得 standardOutput
和 standardError
都被分配一个特殊类型的 StreamReader
(例如PipeStreamReader
).
Essentially, what Microsoft needs to do is change Process.StartWithCreateProcess
such that standardOutput
and standardError
are both assigned a specialised type of StreamReader
(e.g. PipeStreamReader
).
在这个 PipeStreamReader
中,他们需要覆盖两个 ReadBuffer
重载(即首先需要在 StreamReader
中将两个重载都更改为虚拟),这样在读取之前,调用 PeekNamedPipe
来进行实际的窥视.目前,当没有数据可供读取时,FileStream.Read()
(由 Peek()
调用)将阻塞管道读取.虽然具有 0 字节的 FileStream.Read()
在文件上运行良好,但在管道上运行不佳.事实上,.NET 团队错过了管道文档的一个重要部分 - PeekNamedPipe WinAPI.
In this PipeStreamReader
, they need to override both ReadBuffer
overloads (i.e. need to change both overloads to virtual in StreamReader
first) such that prior to a read, PeekNamedPipe
is called to do the actual peek. As it is at the moment, FileStream.Read()
(called by Peek()
) will block on pipe reads when no data is available for read. While a FileStream.Read()
with 0 bytes works well on files, it doesn't work all that well on pipes. In fact, the .NET team missed an important part of the pipe documentation - PeekNamedPipe WinAPI.
PeekNamedPipe 函数与 ReadFile 函数类似,但有以下例外:
The PeekNamedPipe function is similar to the ReadFile function with the following exceptions:
...
函数总是在单线程应用程序中立即返回,即使管道中没有数据.命名管道句柄的等待模式(阻塞或非阻塞)对函数没有影响.
The function always returns immediately in a single-threaded application, even if there is no data in the pipe. The wait mode of a named pipe handle (blocking or nonblocking) has no effect on the function.
如果没有在框架中解决这个问题,目前最好的办法是推出您自己的 Process 类(围绕 WinAPI 的薄包装就足够了).
The best thing at this moment without this issue solved in the framework would be to roll out your own Process class (a thin wrapper around WinAPI would suffice).
这篇关于StreamReader.Peek 和 Thread.Interrupt 的替代方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!