使用 IPC C# 时如何有效地从管道流中读取

Posted

技术标签:

【中文标题】使用 IPC C# 时如何有效地从管道流中读取【英文标题】:How to Efficiently Read From a Pipe Stream when using IPC C# 【发布时间】:2016-01-22 23:03:18 【问题描述】:

我在下面编写了我的程序的简化版本。进程 A 启动一个子进程(进程 B)。我使用匿名管道来写入有关在进程 B 上运行的方法的进度的信息。同时,我在进程 A 中有一个函数,它不断地从流中读取,以查看是否有来自管道的新更新。如果有,则更新流程 A 上的表单以反映进度。这可以按预期工作,但是我想知道是否有更好的方法来完成此操作,而无需不断检查流以查看进度是否有任何新更新。

/////////////////
///Process A ////
/////////////////

public void LaunchProcessB()

    using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In,
            HandleInheritability.Inheritable))
    
        var _Process = new Process();
        _Process.StartInfo.FileName = exeString;
        _Process.StartInfo.Arguments = pipeServer.GetClientHandleAsString()
        _Process.StartInfo.RedirectStandardOutput = true;
        _Process.StartInfo.RedirectStandardInput = true;
        _Process.StartInfo.CreateNoWindow = true;
        _Process.StartInfo.UseShellExecute = false;
        _Process.Start(); //launches process B

        pipeServer.DisposeLocalCopyOfClientHandle();

        using (StreamReader sr = new StreamReader(pipeServer))
        
            try
            
                while (true)
                
                    string temp = sr.ReadLine();
                    if (temp == null) break;

                    int result;
                    if (Int32.TryParse(temp, out result))
                        ShowDocumentProgress(result);
                    else ShowProgress(temp);
                
            
            catch (Exception)
            
                //error occured when reading from stream.
            
        

        if (!_Process.Responding && !_Process.HasExited)
        
            _Process.Kill();
            return;
        

        _Process.WaitForExit(10000);
    


private void ShowProgressPercent(int percentage)

    if (percentage > currentPercentage)
    
        progressBar.Value = percentage;
    


private void ShowProgress(string progressString)

    labelMessage.Text = progressString;



/////////////////
///Process B ////
/////////////////

private StreamWriter _progressWriter;
private PipeStream _progressPipe;

static int Main(string[] args)

    using (progressPipe = new AnonymousPipeClientStream(PipeDirection.Out, args[0]))
    using (_progressWriter = new StreamWriter(_progressPipe))   
    
        RunLongProcess()
    


private void RunLongProcess() 

    //attaches events to PercentProgress and StageProgress methods.  


private void PercentProgress(int percentage)

    _progressWriter.WriteLine(percentage.ToString());
    _progressPipe.WaitForPipeDrain();


private void StageProgress(string stage) 

    _progressWriter.WriteLine(stage);
    _progressPipe.WaitForPipeDrain();

【问题讨论】:

【参考方案1】:

while 条件不是必需的。只需阅读直到 temp 为空。这是流的结束信号。

将此设为while(true) 循环。

我认为您还需要添加异常处理来捕获终止和切断管道的进程。 !_Process.HasExited && pipeServer.IsConnected 是不够的,因为它可能是真的,但在测试后立即切换为假。

我还会在末尾添加一个WaitForExit,以确保系统在您继续之前处于静默状态。

【讨论】:

感谢您的建议。我更新了我的代码以反映它们。我仍然在 while 循环中保留 pipeServer.IsConnected 检查,以避免抛出大多数异常。我还添加了一项检查以确保该过程仍然响应。 这在两个方面被破坏了:_Process.Responding 是基于 GUI 的,所以可能不对。即使您想要这些语义,如果进程变得无响应,您也可能会丢弃缓冲的数据。其次,即使 pipeServer.IsConnected 变为 false,流读取器也可能缓冲了您将丢失的数据。 if (temp == null) continue; 这应该是 ... break;。您几乎不会在这里看到异常,因为大多数时候远程句柄将被关闭并且您将收到 null。 _Process.Kill(); return; 需要一个 WaitForExit es,因为杀死不是即时的。 IO 可能会流连忘返。此外,您不应该仅仅因为它关闭了它的管道就终止该进程。它可能仍在做事并关闭。也许只有在 WaitForExit 返回 false 之后才杀死? 我不明白为什么 continue 语句应该是一个中断。我一直在检查流中是否有新行。进程 B 不会不断地写入管道流,因此 temp 可以并且将为空。如果我打破了,那么我会过早地跳出循环。 如果检查 _Process.Responding 是错误的想法,那么判断进程是否变得无响应并需要终止的正确方法是什么?

以上是关于使用 IPC C# 时如何有效地从管道流中读取的主要内容,如果未能解决你的问题,请参考以下文章

如何在 BizTalk 管道组件中有效地修改流中的文本?

LINUX FIFO 命名管道 (IPC) 在特定文件描述符处停止写入/读取消息

如何使用管道进行非阻塞 IPC(UART 仿真)

Android有效地从输入流中读取

go语言快速入门 IPC之管道通信 8

将 Windows 命名管道用于 IPC 的一种有效方法