【问题标题】:Event driven stdin in C#C# 中的事件驱动标准输入
【发布时间】:2015-10-09 08:01:34
【问题描述】:

当在stdin stream 上为我自己的进程接收数据时,C# 是否提供事件? Process.OutputDataReceived 之类的东西,只有我需要 InputDataReceived 的事件。

我搜索了高低,并学会了redirect stdin->stdoutmonitor output streams of spawned apps 和大量其他内容,但没有人显示收到标准输入时触发了哪个事件。除非我在 main() 中使用愚蠢的轮询循环。

// dumb polling loop -- is this the only way? does this consume a lot of CPU?
while ((line = Console.ReadLine()) != null && line != "") {
     // do work
}

另外,我需要从流中获取二进制数据,如下所示:

using (Stream stdin = Console.OpenStandardInput())
using (Stream stdout = Console.OpenStandardOutput())
{
    byte[] buffer = new byte[2048];
    int bytes;
    while ((bytes = stdin.Read(buffer, 0, buffer.Length)) > 0) {
        stdout.Write(buffer, 0, bytes);
    }
}

【问题讨论】:

  • 您希望该事件用于流程(从外部)还是您自己的流程?
  • 为什么需要这个活动?除非您编写多线程程序,否则事件的行为与循环读取没有任何不同。
  • @PanagiotisKanavos - 明白了。这是个好主意。

标签: c# ipc stdout stdin


【解决方案1】:

轮询循环不会消耗太多 CPU,因为 ReadLine 会阻塞并等待。将此代码放在自己的工作线程中并从中引发您的事件。据我所知,.NET 中没有这样的功能。

编辑:我一开始就错了。更正:

您实际上可以从标准输入读取二进制数据,正如this SO answer 所说:

要读取二进制文件,最好的方法是使用原始输入流 - 这里显示了标准输入和标准输出之间的“回声”:

using (Stream stdin = Console.OpenStandardInput())
using (Stream stdout = Console.OpenStandardOutput())
{
    byte[] buffer = new byte[2048];
    int bytes;
    while ((bytes = stdin.Read(buffer, 0, buffer.Length)) > 0) {
        stdout.Write(buffer, 0, bytes);
    }
}

【讨论】:

  • 为什么不能使用stdin.Read(..)直接读取二进制数据?为什么需要base64? stdin 流是否仅限于 ASCII 字符?
  • 对不起,我在 stdin.read-binary 主题上错了。我更新了我的答案。
  • 不,他们不是。不需要 Base64。您可以使用ReadAsync 之类的异步方法来完全避免阻塞并完全实现您想要的。
  • @PanagiotisKanavos - 你能添加一个异步读取流的答案吗?
  • @Geotarget first,你为什么要这样做?除非您有另一个线程处理输入,否则异步读取数据不会有任何好处
【解决方案2】:

这是一种异步方法。与 OutputDataReceived 一样,回调在换行符上运行。对于二进制,streaming to base64 可能有效。将其切换为二进制流更难,因为您不能只检查换行符。

using System.Diagnostics;
using System.Threading.Tasks;

public static void ListenToParent(Action<string> onMessageFromParent)
{
    Task.Run(async () =>
    {
        while (true) // Loop runs only once per line received
        {
            var text = await Console.In.ReadLineAsync();
            onMessageFromParent(text);
        }
    });
}

这是我的父应用设置子进程的方式:

var child = new Process()
{
    EnableRaisingEvents = true,
    StartInfo =
    {
        FileName = ..., // .exe path
        RedirectStandardOutput = true,
        RedirectStandardInput = true,
        UseShellExecute = false,
        CreateNoWindow = true
    },
};

child.Start();
child.BeginOutputReadLine();

...以及它如何向子进程发送一行:

child.StandardInput.WriteLine("Message from parent");

【讨论】:

    猜你喜欢
    • 2017-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多