挂在管道上的 WaitForConnectionAsync()
Hanging with WaitForConnectionAsync() on pipe
我正在通过命名管道与另一个进程通信。管道服务器用C#实现,客户端用C写,服务器是WPF应用
我需要创建一个 NamedPipeServerStream
并等待(同步)最多 1 秒以便客户端连接。然后我需要知道客户端是否连接。
作为 NamedPipeServerStream
等待客户端连接的唯一方法 cancel/timeout 是通过其异步 WaitForConnectionAsync
方法 - 它需要一个 CancellationToken
- 我'我已经实现了我认为的同步等待,如下所示:
public bool WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = WaitForConnectionAsyncSyncWrapper().Result;
}
catch (AggregateException e)
{
log.Write("Error waiting for pipe client connect: " + e.InnerException.Message);
}
return result;
}
private async Task<bool> WaitForConnectionAsyncSyncWrapper()
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
管道定义如下:NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
WaitOneSecondForClientConnect()
函数在 UI 线程上运行。
应该使其同步的是在 Task<bool>
returns 上访问异步 WaitForConnectionAsyncSyncWrapper()
函数的 Result
属性。为了访问结果,异步函数必须完全返回,并且在 await pipe.WaitForConnectionAsync(cts.Token);
完成后函数恢复之前它无法执行 return pipe.IsConnected
行。至少这是我的理解。
所以问题是:虽然客户端程序说它打开了我服务器的管道(当然在上面的代码执行之前已经创建),WaitOneSecondForClientConnect()
从来没有 returns。如果我闯入服务器,它就在这条线上:result = WaitForConnectionAsyncSyncWrapper().Result;
。
所以我猜它正在等待任务的结果可用,如果客户端在 1 秒内连接,它应该是 pipe.IsConnected
的值,或者当我访问时它应该抛出 AggregateException
如果 await
已完成,因为令牌已被取消(1 秒后)。但它完全挂了。
另一方面,如果我在开始之前取消令牌,例如通过在调用 await pipe.WaitForConnectionAsync(cts.Token);
之前放置 Thread.Sleep(2000);
,然后成功取消连接(我认为它甚至不会尝试启动,因为令牌已经被取消) - 访问 Result
属性 抛出 AggregateException
,等等...
有几点需要注意。
- 如果我将
WaitOneSecondForClientConnect()
的内容替换为
标准同步 pipe.WaitForConnection();
,它每次都有效 -
即客户端连接,函数 returns.
- 在我编写的测试程序中,最初是为了让 async/sync 正常工作,以这种同步-异步方式连接每次都有效。这是一个控制台程序,而不是我的真实程序 WPF。下面列出了测试程序的相关代码。
- 上面发布的代码实际上已经运行了几次,可能失败了 30-40 次。
- 如果我的客户没有打开我的管道,我的 "real" 代码仍然挂起,而我的测试代码等待指定的时间段,然后按预期打印 "Connection failed."(见下文)-正是我的真实代码中应该发生的行为。
有效的测试代码:
var pipe = new NamedPipeServerStream("SemiUsefulPipe_" + pid.ToString() + "ctest", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
// ... dll containing pipe client is injected in client process at this point.
try
{
var result = ConnectAsync(pipe).Result;
}
catch (AggregateException)
{
Console.WriteLine("Connection failed.");
}
...
private static async Task<bool> ConnectAsync(NamedPipeServerStream pipe)
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
您不能像这样混合使用异步和非异步方法。发生的情况是您的 WaitOneSecondForClientConnect
方法正在等待 WaitForConnectionAsyncSyncWrapper
方法完成。但是那个人需要它的调用线程是空闲的,这样它才能重新水化原始上下文。所以你刚刚制造了一个僵局。有关详细信息,请参阅 https://msdn.microsoft.com/en-us/magazine/jj991977.aspx。
相反,您需要一直异步。
public async Task<bool> WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = await WaitForConnectionAsyncSyncWrapper();
}
catch (Exception e)
{
log.Write("Error waiting for pipe client connect: " + e.Message);
}
return result;
}
我正在通过命名管道与另一个进程通信。管道服务器用C#实现,客户端用C写,服务器是WPF应用
我需要创建一个 NamedPipeServerStream
并等待(同步)最多 1 秒以便客户端连接。然后我需要知道客户端是否连接。
作为 NamedPipeServerStream
等待客户端连接的唯一方法 cancel/timeout 是通过其异步 WaitForConnectionAsync
方法 - 它需要一个 CancellationToken
- 我'我已经实现了我认为的同步等待,如下所示:
public bool WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = WaitForConnectionAsyncSyncWrapper().Result;
}
catch (AggregateException e)
{
log.Write("Error waiting for pipe client connect: " + e.InnerException.Message);
}
return result;
}
private async Task<bool> WaitForConnectionAsyncSyncWrapper()
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
管道定义如下:NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
WaitOneSecondForClientConnect()
函数在 UI 线程上运行。
应该使其同步的是在 Task<bool>
returns 上访问异步 WaitForConnectionAsyncSyncWrapper()
函数的 Result
属性。为了访问结果,异步函数必须完全返回,并且在 await pipe.WaitForConnectionAsync(cts.Token);
完成后函数恢复之前它无法执行 return pipe.IsConnected
行。至少这是我的理解。
所以问题是:虽然客户端程序说它打开了我服务器的管道(当然在上面的代码执行之前已经创建),WaitOneSecondForClientConnect()
从来没有 returns。如果我闯入服务器,它就在这条线上:result = WaitForConnectionAsyncSyncWrapper().Result;
。
所以我猜它正在等待任务的结果可用,如果客户端在 1 秒内连接,它应该是 pipe.IsConnected
的值,或者当我访问时它应该抛出 AggregateException
如果 await
已完成,因为令牌已被取消(1 秒后)。但它完全挂了。
另一方面,如果我在开始之前取消令牌,例如通过在调用 await pipe.WaitForConnectionAsync(cts.Token);
之前放置 Thread.Sleep(2000);
,然后成功取消连接(我认为它甚至不会尝试启动,因为令牌已经被取消) - 访问 Result
属性 抛出 AggregateException
,等等...
有几点需要注意。
- 如果我将
WaitOneSecondForClientConnect()
的内容替换为 标准同步pipe.WaitForConnection();
,它每次都有效 - 即客户端连接,函数 returns. - 在我编写的测试程序中,最初是为了让 async/sync 正常工作,以这种同步-异步方式连接每次都有效。这是一个控制台程序,而不是我的真实程序 WPF。下面列出了测试程序的相关代码。
- 上面发布的代码实际上已经运行了几次,可能失败了 30-40 次。
- 如果我的客户没有打开我的管道,我的 "real" 代码仍然挂起,而我的测试代码等待指定的时间段,然后按预期打印 "Connection failed."(见下文)-正是我的真实代码中应该发生的行为。
有效的测试代码:
var pipe = new NamedPipeServerStream("SemiUsefulPipe_" + pid.ToString() + "ctest", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
// ... dll containing pipe client is injected in client process at this point.
try
{
var result = ConnectAsync(pipe).Result;
}
catch (AggregateException)
{
Console.WriteLine("Connection failed.");
}
...
private static async Task<bool> ConnectAsync(NamedPipeServerStream pipe)
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
您不能像这样混合使用异步和非异步方法。发生的情况是您的 WaitOneSecondForClientConnect
方法正在等待 WaitForConnectionAsyncSyncWrapper
方法完成。但是那个人需要它的调用线程是空闲的,这样它才能重新水化原始上下文。所以你刚刚制造了一个僵局。有关详细信息,请参阅 https://msdn.microsoft.com/en-us/magazine/jj991977.aspx。
相反,您需要一直异步。
public async Task<bool> WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = await WaitForConnectionAsyncSyncWrapper();
}
catch (Exception e)
{
log.Write("Error waiting for pipe client connect: " + e.Message);
}
return result;
}