管道上挂起WaitForConnectionAsync()

本文关键字:WaitForConnectionAsync 挂起 管道 | 更新日期: 2023-09-27 18:12:08

我正在通过命名管道与另一个进程通信。管道服务器是用C#实现的,客户端是用C编写的。服务器是一个WPF应用程序。

我需要创建一个NamedPipeServerStream,并等待(同步(长达1秒,等待客户端连接。然后我需要知道客户端是否连接。

由于NamedPipeServerStream取消/超时等待客户端连接的唯一方法是通过其异步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线程上运行。

应该使其同步的是访问异步WaitForConnectionAsyncSyncWrapper()函数返回的Task<bool>上的Result属性。为了访问Result,异步函数必须已完全返回,并且在await pipe.WaitForConnectionAsync(cts.Token);完成后恢复函数之前,它不能执行return pipe.IsConnected行。至少这是我的理解。

所以问题是:尽管客户端程序说它已经打开了我的服务器管道(当然,在上面的代码执行之前就已经创建了(,但WaitOneSecondForClientConnect()永远不会返回。如果我闯入服务器,它就在这行:result = WaitForConnectionAsyncSyncWrapper().Result;

所以我猜它在等待Task的Result可用,如果客户端在1秒内连接,它应该是pipe.IsConnected的值,或者如果await已经完成,因为令牌已经取消(1秒后(,它应该在我访问它时抛出AggregateException。但它只是完全悬着。

另一方面,如果我在令牌启动之前取消它,例如在调用await pipe.WaitForConnectionAsync(cts.Token);之前放置Thread.Sleep(2000);,则连接被成功取消(我认为它甚至没有尝试启动,因为令牌已经被取消(-访问Result属性会抛出AggregateException

有几点需要注意。

  • 如果我将WaitOneSecondForClientConnect()的内容替换为标准同步pipe.WaitForConnection();,每次都能工作-即客户端连接并且函数返回
  • 在我编写的一个测试程序中,最初是为了让这种异步/同步的东西发挥作用,每次都以这种同步-异步的方式进行连接。这是一个控制台程序,而不是我真正的程序WPF。测试程序的相关代码如下所示
  • 上面发布的代码实际上已经运行了几次,可能失败了30-40次
  • 如果我的客户端没有打开我的管道,我的"真实"代码仍然挂起,而我的测试代码等待指定的时间段,然后按预期打印"连接失败"(见下文(-这正是我的真实代码中应该发生的行为

有效的测试代码:

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

管道上挂起WaitForConnectionAsync()

不能像这样混合异步和非异步方法。发生的情况是,您的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;
}
相关文章:
  • 没有找到相关文章