Альтернатива StreamReader.Peek и Thread.Interrupt - PullRequest
3 голосов
/ 29 декабря 2010

Краткое предисловие к тому, что я пытаюсь сделать. Я хочу запустить процесс и запустить два потока для мониторинга 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);
}

Этот блок кода выполняется в одном потоке, который сейчас не является фоновым. Есть похожая тема для потока StandardError. Я использую этот метод вместо того, чтобы слушать OutputDataReceived и ErrorDataReceived, потому что в Mono была проблема, из-за которой эти события не всегда срабатывали должным образом, и, несмотря на то, что теперь это кажется исправленным, мне нравится, что этот метод гарантирует, что я читаю и пишу все последовательно.

ShutdownClient с True просто пытается уничтожить оба потока. К сожалению, я нашел единственный способ сделать эту работу - использовать прерывание для объектов stdErrThread и stdOutThread. В идеале peek не должен блокироваться, и я мог бы просто использовать событие ручного сброса для проверки новых данных в stdOut или stdIn, а затем просто умереть, когда событие перевернуто.

Я сомневаюсь, что это лучший способ сделать это. Есть ли способ выполнить это без использования прерывания?

Я хотел бы изменить, потому что я только что увидел в своих журналах, что пропустил исключение ThreadInterruptException, выброшенное в Utlities.ConsoleOut. Это просто делает System.Console.Write, если статическая переменная истинна, но я предполагаю, что это где-то блокирует.

редактирует:

Эти потоки являются частью родительского потока, который массово запускается сервером по запросу. Поэтому я не могу установить потоки StdOut и StdErr на фон и убить приложение. Я мог бы убить родительский поток с главного сервера, но это опять-таки застряло бы с блокировкой Peek.

Добавлена ​​информация о том, что это сервер.

Кроме того, я начинаю понимать, что лучшим методом решения для очереди может быть окончательное решение.

Ответы [ 4 ]

2 голосов
/ 14 июля 2011

Я могу сказать, что весь этот беспорядок связан с тем, что Peek блокирует.Вы действительно пытаетесь исправить что-то, что в корне сломано в фреймворке, и это никогда не бывает легким (то есть не грязным взломом).Лично я бы исправил корень проблемы, которая заключается в блокировке Peek.Mono следовал бы за реализацией Microsoft и, таким образом, столкнулся бы с той же проблемой.

Хотя я точно знаю, как решить проблему, если мне разрешат изменить исходный код платформы, обходной путь является длительным и длительным.

Но это так.

По сути, Microsoft нужно изменить Process.StartWithCreateProcess таким образом, чтобы standardOutput и standardError были присвоены специализированный тип StreamReader (например, PipeStreamReader).

В этом PipeStreamReader им необходимо переопределить обе ReadBuffer перегрузки (т. Е. Необходимо сначала изменить обе перегрузки на виртуальные в StreamReader) так, чтобы перед чтением PeekNamedPipeпризван сделать реальный взгляд.Как и на данный момент, FileStream.Read() (вызываемый Peek()) будет блокировать чтение канала, когда данные для чтения недоступны.В то время как FileStream.Read() с 0 байтами хорошо работает с файлами, он не очень хорошо работает с каналами.Фактически, команда .NET пропустила важную часть документации канала - PeekNamedPipe WinAPI.

Функция PeekNamedPipe похожа на функцию ReadFile со следующими исключениями:

...

Функция всегда сразу возвращается в однопоточном приложении, , даже если в канале нет данных .Режим ожидания дескриптора именованного канала (блокирующий или неблокирующий) не влияет на функцию.

Лучшим решением на данный момент без решения этой проблемы в среде будет развертывание собственного процесса.class (достаточно тонкой оболочки вокруг WinAPI).

0 голосов
/ 17 июля 2015

Вы строите сервер.Вы хотите избежать блокировки.Очевидным решением является использование асинхронных 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);
}

Нет необходимости тратить потоки на выполнение операций ввода-вывода:)

0 голосов
/ 30 декабря 2010

Избавьтесь от просмотра и используйте метод ниже, чтобы прочитать из выходных потоков процесса.ReadLine () возвращает ноль, когда процесс заканчивается.Чтобы присоединиться к этому потоку с вызывающим потоком, либо дождитесь завершения процесса, либо убейте процесс самостоятельно.ShutdownClient () должен просто завершить () процесс, который приведет к тому, что другой поток, считывающий StdOut или StdErr, также завершится.

    private void ReadToEnd()
    {
        string nextLine;
        while ((nextLine = stream.ReadLine()) != null)
        {
             output.WriteLine(nextLine);
        }
    }
0 голосов
/ 29 декабря 2010

Почему бы вам просто не установить оба потока в фоновый режим, а затем убить приложение?Это приведет к немедленному закрытию обоих потоков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...