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

Why does reading from pipe block the process?

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

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

WriteAsync 加载负载后,我必须在服务器上做些什么吗?我需要冲洗什么吗?

服务器

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);

P.S 但是,再次使用 serverReadAsync 会阻塞 server.So 将 NamedPipeServerStreamClientStream 包装到 StreamReader 和 [=21 时会出现问题=].

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

一个 lot 已经写了关于此事的文章:

Don't Block on Async Code

引用:

There are two best practices [...] that avoid this situation:

  • In your “library” async methods, use ConfigureAwait(false) wherever possible.
  • Don’t block on Tasks; use async all the way down.

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

因此,最简单的解决方案是在 UI 中 运行 不知道的代码中的所有地方 添加 ConfigureAwait(false) ,或使用 async everywhere(永远不要在你的整个代码中阻塞任何任务,真的无处不在,包括在你不拥有的代码中)。

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

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