为啥从管道读取会阻塞进程?

Posted

技术标签:

【中文标题】为啥从管道读取会阻塞进程?【英文标题】:Why does reading from pipe block the process?为什么从管道读取会阻塞进程? 【发布时间】:2019-06-14 08:40:47 【问题描述】:

我有两个进程(两个都是.NET Framework 应用程序),我正在尝试使用Named Pipes 进行通信 客户端连接到管道,但是当它尝试ReadAsync服务器发送的消息时,它开始无差别地等待,即使服务器已经发送了一条消息!!

令我惊讶的是,如果我关闭 Server 应用程序,Client 最终 继续 到下一行代码(在 ReadAsync 行之后),阅读 0 字节。

WriteAsync-ing 有效载荷之后,我必须在服务器上做些什么吗?我需要冲洗吗?

服务器

static async Task Main(string[] args) 

         var server = new NamedPipeServerStream(
             "somePipe",
              PipeDirection.InOut, 
              2, 
              PipeTransmissionMode.Message
              );

         await server.WaitForConnectionAsync();

         using (StreamReader reader = new StreamReader(server)) 
              using (StreamWriter writer = new StreamWriter(server)) 

                 await writer.WriteAsync("Hello from server"); 

                 char[] buffer = new char[20];
                 var read = await reader.ReadAsync(buffer, 0, buffer.Length);
                
         

客户

 static async Task Main() 

    NamedPipeClientStream client = new NamedPipeClientStream(
             ".",
             "somePipe",
             PipeDirection.InOut,
             PipeOptions.Asynchronous);

    await client.ConnectAsync();

    try 
    using (StreamReader reader = new StreamReader(client)) 
        using (StreamWriter writer = new StreamWriter(client)) 

          var buffer = new char[20];
          int readChars = await reader.ReadAsync(buffer, 0,buffer.Length); //starts waiting indifinetly , until i close the Server

          await writer.WriteAsync("From Client");

        
     
      catch (Exception ex) 

       throw;
     

更新 看来是直接用NamedPipeServerStream写,而不是服务端的StreamWriter,让客户端获取数据!

服务器更改

byte[] data=Encoding.UTF8.GetBytes("Hello from server");
server.WriteAsync(data,0,data.Length);

附言 但是再次使用serverReadAsync 会阻塞服务器。因此,将NamedPipeServerStreamClientStream 包装到StreamReaderStreamWriter 时会出现问题。

【问题讨论】:

StreamWriter 有一个内部缓冲区。在写完消息和/或设置AutoFlush = true 后,您是否尝试过在作者上显式调用Flush() 我在读写之前尝试过使用Flush 您可以尝试在所有 await(ed) 调用的末尾添加 .ConfigureAwait(false) 吗? 添加.ConfigureAwait 一个客户端的ConnectAsync 和服务器的WriteAsync 似乎没有问题。您能进一步解释一下吗?我以前没有用过这个,我不明白这种行为。似乎甚至不再需要Flush @Simon Mourier 发布答案,我会接受它。它解决了我的问题,非常感谢。另外,如果您能解释为什么我需要ConfigureAwait(false),我将非常感激,或者为什么StreamReaderWriter 不起作用。 【参考方案1】:

此阻塞/锁定问题是由 async/await 语法糖暗中生成的代码引起的。 如果你不添加ConfigureAwait(false),编译器会生成一些对 UI 应用程序来说很酷的东西(UI 是一个通用术语,可能是 ASP.NET - 而不是核心 - 、WPF、Winforms 等),但这可能是致命的否则。

很多已经写了这件事:

Don't Block on Async Code

引用:

有两种最佳做法 [...] 可以避免这种情况:

在“库”异步方法中,在任何地方使用 ConfigureAwait(false) 可能。 不要阻止任务;一直使用异步。

Parallel Computing - It's All About the SynchronizationContext

Best practice to call ConfigureAwait for all server-side code

Should I call ConfigureAwait(false) on every awaited operation

所以,最简单的解决方案是在你的代码中添加ConfigureAwait(false)everywhere,或者使用asynceverywhere(从不阻止你的整个代码中的任何任务,真的无处不在,包括你不拥有的代码)。

恕我直言,这太荒谬了...如果我忘记在每个 async 调用后面添加它,我个人会使用一个对我大喊大叫的 Visual Studio 扩展:-)

请注意,有一些替代方案,例如: An alternative to ConfigureAwait(false) everywhere

【讨论】:

以上是关于为啥从管道读取会阻塞进程?的主要内容,如果未能解决你的问题,请参考以下文章

C、是不是可以阻塞进程直到管道再次打开?

管道编写器可以判断阅读器何时阻塞吗?

Linux pipe():从管道读取并不总是解除对写入器的阻塞

如何测试输出到 std::cout (连接到管道)是不是会阻塞

Java中的命名管道和多线程

read() 不会阻塞在没有 O_NONBLOCK 标志的情况下打开的空 FIFO