为啥从管道读取会阻塞进程?
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);
附言
但是再次使用server
到ReadAsync
会阻塞服务器。因此,将NamedPipeServerStream
和ClientStream
包装到StreamReader
和StreamWriter
时会出现问题。
【问题讨论】:
StreamWriter
有一个内部缓冲区。在写完消息和/或设置AutoFlush = true
后,您是否尝试过在作者上显式调用Flush()
?
我在读写之前尝试过使用Flush
。
您可以尝试在所有 await(ed) 调用的末尾添加 .ConfigureAwait(false)
吗?
添加.ConfigureAwait
一个客户端的ConnectAsync
和服务器的WriteAsync
似乎没有问题。您能进一步解释一下吗?我以前没有用过这个,我不明白这种行为。似乎甚至不再需要Flush
。
@Simon Mourier 发布答案,我会接受它。它解决了我的问题,非常感谢。另外,如果您能解释为什么我需要ConfigureAwait(false)
,我将非常感激,或者为什么StreamReader
和Writer
不起作用。
【参考方案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,或者使用async
everywhere(从不阻止你的整个代码中的任何任务,真的无处不在,包括你不拥有的代码)。
恕我直言,这太荒谬了...如果我忘记在每个 async
调用后面添加它,我个人会使用一个对我大喊大叫的 Visual Studio 扩展:-)
请注意,有一些替代方案,例如: An alternative to ConfigureAwait(false) everywhere
【讨论】:
以上是关于为啥从管道读取会阻塞进程?的主要内容,如果未能解决你的问题,请参考以下文章
Linux pipe():从管道读取并不总是解除对写入器的阻塞