[C#] Почему рабочий поток переходит в состояние WaitSleepJoin?

ms_nadin

В таком примере - в рабочем потоке запускается утилита tasklist:
 
using System;
using System.Diagnostics;
using System.Threading;

namespace ThreadProcessExample {
class Program {
static void Main {

ProcessStartInfo ProcInfo = new ProcessStartInfo("tasklist") {
UseShellExecute = false,
RedirectStandardOutput = true
};
Process Proc = new Process { StartInfo = ProcInfo };

Thread th = new Thread(PerformProcess);
th.Start(Proc);
Thread.Sleep(1000);
string answer = "";
while (answer != "y") {
Console.WriteLine("Working thread state is {0}", th.ThreadState);
Console.WriteLine("Abort thread? (y/n)");
answer = Console.ReadLine.ToLower;
if (answer == "y" && th.ThreadState != System.Threading.ThreadState.Stopped)
th.Abort;
}
Console.Read;
}

static void PerformProcess(object Proc) {
if (!(Proc is Process return;
try {
Process)Proc).Start;
Process)Proc).WaitForExit;
Console.WriteLineProcess)Proc).StandardOutput.ReadToEnd;
}
catch (ThreadAbortException) {
if (!Process)Proc).HasExited) Process)Proc).Kill;
Console.WriteLine("ThreadAbortException. \r\n"+Process)Proc).StandardOutput.ReadToEnd;
}
}
}
}

Dasar

> Почему рабочий поток переходит в состояние WaitSleepJoin?
а ты куда хочешь чтобы он перешел?
вот эти оба метода переводят в WaitSleepJoin
   Thread.Sleep(1000);
  Console.ReadLine.

zorin29

Потому что tasklist запускается не "в рабочем потоке", а в отдельном процессе, ПОД УПРАВЛЕНИЕМ рабочего потока. А рабочий поток ожидает завершения процесса. Естественно, делает он это в состоянии WaitSleepJoin.

ms_nadin

Я хочу чтобы рабочий поток перешел в состояние Stopped, (К примеру, если запускаемую утилиту tasklist заменить на ipconfig, то так и происходит).
Thread.Sleep и Console.ReadLine не влияют на запущенный рабочий поток, т.к. выполняются в основном потоке.

ms_nadin

Почему тогда, к примеру, ping или ipconfig так не делают?

Dasar

Почему тогда, к примеру, ping или ipconfig так не делают?
переходит в stopped, когда запущенный процесс завершается.
ping/ipconfig - у тебя завершаются, а tasklist не хочет.
скорее всего tasklist не хочет потому что у него вывод длинный, и он ждет когда ты у него вывод считаешь.
а вывод ping/ipconfig влазит в буфер, поэтому проскакивает.

Dasar

попробуй вот эту строчку закомментировать
Process)Proc).WaitForExit;

ms_nadin

Да, так и есть, спасибо.
Здесь предлагают такое решение:
This problem can be solved by moving the ReadToEnd before the WaitForExit as follows.
Process p = new Process;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "test.exe";
p.Start;
string output = p.StandardOutput.ReadToEnd;
p.WaitForExit;

Только непонятно, почему нет возможности изменить размер буфера?

Dasar

Только непонятно, почему нет возможности изменить размер буфера?
это скорее всего виндовый буфер, чуть ли не в ядре, там его проблематично поменять.

ms_nadin

А вот это получше решение - асинхронное считывание перенаправленного потока :
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

namespace ThreadProcessExample {
class Program {
static StringBuilder _output = new StringBuilder;
static void Main {

ProcessStartInfo ProcInfo = new ProcessStartInfo("tasklist") {
UseShellExecute = false,
RedirectStandardOutput = true
};
Process Proc = new Process { StartInfo = ProcInfo };
Proc.OutputDataReceived += new DataReceivedEventHandler(Proc_OutputDataReceived);
Thread th = new Thread(PerformProcess);
th.Start(Proc);
string answer = "";
while (answer != "y") {
Console.WriteLine("Working thread state is {0}", th.ThreadState);
Console.WriteLine("Abort thread? (y/n)");
answer = Console.ReadLine.ToLower;
if (answer == "y" && th.ThreadState != System.Threading.ThreadState.Stopped){
th.Abort;
}
}
Console.Read;
}

static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e) {
_output.Append(e.Data != "" ? e.Data+Environment.NewLine : "");
}

static void PerformProcess(object Proc) {
if (!(Proc is Process return;
try{
Process)Proc).Start;
Process) Proc).BeginOutputReadLine;
Process)Proc).WaitForExit;
Console.WriteLine(_output);
}
catch (ThreadAbortException) {
if (!Process)Proc).HasExited) Process)Proc).Kill;
Console.WriteLine("ThreadAbortException. \r\n"+_output);
}
}
}
}

Только пока не могу понять как в этом случае справляться процессами типа "ping localhost -t". Как сделать так, чтобы можно было выполнить Abort для рабочего потока?

Dasar

> Как сделать так, чтобы можно было выполнить Abort для рабочего потока?
только не рабочего потока, а запущенного процесса.
можешь кильнуть его: process.Kill,
для ping -t кошернее послать ему ctrl-c ( proc.StandardInput.WriteLine("\x3"); )

ms_nadin

Да, понятно, что можно.
Просто идея была в том, чтобы останавливать процесс в обработчике исключения ThreadAbortException, которое инициируется при вызове Thread.Abort однако с Process.WaitForExit этот способ не подходит.

Попробовал вместо WaitForExit использовать перегрузку Process.WaitForExit(int milliseconds) и заработало как ожидалось: исключение ThreadAbortException инициируется, даже если не истек указанный период времени.
Оставить комментарий
Имя или ник:
Комментарий: