Alternative to StreamReader.Peek and Thread.Interrupt

A quick introduction to what I'm trying to do. I want to start the process and start two threads to control stderr and stdin. Each stream destroys the bit of the stream and then launches it in NetworkStream. If there is an error in any thread, both threads must die immediately.

Each of these processes, with the stdout and stdin monitoring flows, is allocated by the main server process. The reason this gets complicated is that at any given time, 40 or 50 of these processes can easily be. Only during the morning bursts of a reboot, there are more than 50 connections, but in fact it should handle 100 or more. I am testing 100 concurrent 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); } 

This code block runs in a single thread, which is not Background now. A similar stream for a StandardError stream. I use this method instead of listening to OutputDataReceived and ErrorDataReceived because there was a problem in Mono that caused these events to not always fire properly, and although they seem to be fixed, I like that this method ensures that I I read and write everything in sequence.

ShutdownClient with True is simply trying to kill both threads. Unfortunately, the only way I found this work is to use an interrupt for the stdErrThread and stdOutThread objects. Ideally, peek will not block, and I could just use the manual reset event to continue 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 this. Is there a way to accomplish this without using an interrupt?

I would like to change because I just saw in my logs that I missed the ThreadInterruptException inside Utlities.ConsoleOut. It just does System.Console.Write if the static variable is true, but I assume it blocks somewhere.

Changes:

These threads are part of the parent thread, which is started massively by the server upon request. Therefore, I cannot install the StdOut and StdErr threads in the background and kill the application. I could kill the parent thread from the main server, but this will become sticky again with Peek locking.

Added information about this as a server.

I'm also starting to realize that the best Queuing method for queries can be the ultimate solution.

+3
c # mono
source share
4 answers

I can say that all this mess stems from what Peek blocks. Are you really trying to fix something fundamentally broken within the framework, and it is never easy (i.e., not a dirty hack). Personally, I would fix the root of the problem, which is Peek lock. Mono would have followed Microsoft's implementation, and thus would have received the same problem.

Although I know exactly how to fix the problem if I am allowed to modify the source code of the environment, the workaround is long and time consuming.

But here it goes.

Essentially, Microsoft has to do this in order to modify Process.StartWithCreateProcess so that standardOutput and standardError assigned the specialized StreamReader type (e.g. PipeStreamReader ).

In this PipeStreamReader they need to be redefined as ReadBuffer overloads (i.e., first you need to change both overloads to virtual in StreamReader ) in order to read PeekNamedPipe before reading in order to make a real view, As at the moment, FileStream.Read() (called Peek() ) will be blocked when reading pipes when data for reading is not available. Although FileStream.Read() with 0 bytes works well with files, it does not work so well on pipes. In fact, the .NET team missed an important piece of documentation on the channels - PeekNamedPipe WinAPI.

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 handset . The standby time of a named pipe descriptor (blocking or non-blocking) does not affect the function.

The best thing at this point without this problem being solved within the framework would be to deploy our own Process class (a fairly thin shell around WinAPI).

+2
source share

Why don't you just set both threads around and then kill the application? This will cause both threads to close immediately.

0
source share

You are creating a server. You want to avoid blocking. The obvious solution is to use asynchronous APIs:

 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); } 

No need to waste threads on I / O :)

0
source share

Get rid of peek and use the method below to read from the output of the process. ReadLine () returns null when the process terminates. To join this thread using the calling thread, wait until the process terminates or the process terminates. ShutdownClient () should just kill () the process, which will also cause another thread to read StdOut or StdErr.

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

All Articles