最近因为测试taskBus技术,需要在进程中重定向子进程(c#开发)的标准输入(stdin)。这样,C#子进程中直接可以Console.Read出主进程给的数据。
由于前期在C++、Python2/3、Node.JS等语言上均实现了子进程,而且非常简单,所以也就没当回事儿。没想到,CSharp实现起来却是困难重重!这里特此记录:
- 如果主进程启动子进程后,没有向C#子进程的stdin写入任何东西,而是把stdin放在那里不管,则会导致子进程停在 Read()函数里一直等待。
- 一旦进入上述等待状态,即使后续写入数据,也无法恢复。
这个问题超级诡异,我认为是.Net的BUG(4.6)。我在一个老外的博客里找到了几乎一样的遭遇:
https://daveaglick.com/posts/capturing-standard-input-in-csharp
最终借鉴Dave的代码,实现了读取字节的函数:
public byte[] read_from_stdin(int size)
{
byte[] buffer = new byte[size];
int red = 0;
System.IO.Stream stream = Console.OpenStandardInput();
int read = -1;
AutoResetEvent gotInput = new AutoResetEvent(false);
while (read< size)
{
Thread inputThread = new Thread(() =>
{
try
{
read = stream.Read(buffer, red, buffer.Length - red);
red += read;
gotInput.Set();
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
})
{
IsBackground = true
};
inputThread.Start();
// Timeout expired?
if (!gotInput.WaitOne(100))
inputThread.Abort();
}
return buffer;
}
主要的替代解决(很不优雅)是采用一个线程读取,一个线程监视,不管成不成功,100毫秒后都强制杀死读取线程。相关工程文件见
https://github.com/goldenhawking/taskBus/blob/master/module_templates/csharp/Program.cs