【问题标题】:How do I cancel a PIPE operation?如何取消 PIPE 操作?
【发布时间】:2016-01-20 21:03:23
【问题描述】:

我有一个 C++ 和 C# 应用程序,我使用命名管道相互发送命令。它运行良好,直到我注意到我无法取消 Read() 调用,我使用了一个停止变量,但没有注意到这不是我所需要的,因为它无法读取停止变量状态,直到下车Read() 打电话。我发现我会在CreateNamedPipe() 调用中使用PIPE_NOWAIT 属性。当我添加它时,C# 抛出一个 System.NullReferenceException,因为 FileStream 为空,它是从 new FileStream(clientHandle, FileAccess.ReadWrite, BUFFER_SIZE, true); 创建的,其中 clientHandle 创建如下:

   private void Listen()
        {
            while (!exitLoop)
            {
                clientHandle = CreateNamedPipe(this.pipeName,
                                                DUPLEX | FILE_FLAG_OVERLAPPED,
                                                PIPE_NOWAIT,
                                                255,
                                                BUFFER_SIZE,
                                                BUFFER_SIZE,
                                                0,
                                                IntPtr.Zero);

                if (clientHandle.IsInvalid)
                {
                    return;
                }

                int ok = ConnectNamedPipe(clientHandle, IntPtr.Zero);

                //could not connect client
                if (ok == 0) // here's the error, getLastError() = ERROR_PIPE_LISTENING
                {
                    return;
                }
                 stream = new FileStream(clientHandle, FileAccess.ReadWrite, BUFFER_SIZE, true);
              // ....
        }

如果重要的话,在 C++ 中管道是这样创建的:

 hPipe1 = CreateFile(lpszPipename1,
                       GENERIC_WRITE,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_OVERLAPPED,
                       NULL);
    if (!IsValidPipe(hPipe1))
    {
         openError();
         return;
    }

    hPipe2 = CreateFile(lpszPipename2,
                       GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_OVERLAPPED,
                       NULL);

所以我的问题是:在ConnectNamedPipe() 调用之后,错误是ERROR_PIPE_LISTENING,发生在我添加PIPE_NOWAIT 之后。为什么会出现这个错误?我该如何解决?这是添加支持以取消命名管道操作的正确方法吗?我会杀死 Listen() 正在运行的 theread,但我读到它不是一个好的实践(它甚至也不起作用)。

注意:我正在处理现有代码库,出于时间原因,我希望避免使用 NamedPipeClientStream 重写所有内容。

【问题讨论】:

  • 在非阻塞模式下,ERROR_PIPE_LISTENING 是正常的记录行为;这意味着“目前没有客户端正在尝试连接,请稍后再试”。但是无论如何,您不应该同时使用非阻塞模式和异步模式。异步模式是首选,在这种情况下更是如此,因为 FileStream 类不太可能支持非阻塞模式。
  • C# 应用程序,即客户端,已打开。这就是为什么我没有得到 ConnectNamedPipe() 返回值的原因。如果我删除 PIPE_NOWAIT 属性并使用 0(与 PIPE_WAIT 相同,默认值),这会很好。我试过这个(pastebin.com/03GhwRnJ)只是想看看它是否在尝试一段时间后连接,但它没有。我将尝试清理同时使用异步模式和非阻塞模式的烂摊子......
  • 该 pastebin 代码将不起作用,因为无论使用 ERROR_PIPE_LISTENING (还没有客户端)还是 ERROR_PIPE_CONNECTED (成功!),调用都会返回零,并且您无法区分这两种情况。但这几乎没有实际意义。你真的需要删除PIPE_NOWAIT。希望 mksteve 的回答能为您提供另一种选择。
  • 只是想看看每个ConnectNamedPipe() 之后的最后一个 Windows 错误代码是什么,我运行了这个(pastebin.com/Umk4R4hg)。我得到ERROR_PIPE_LISTENING 一段时间然后ERROR_PIPE_CONNECTED。如果 Windows 最后一个错误代码是 ERROR_PIPE_CONNECTED 则中断循环,我稍后会在 stream.Read() 调用时遇到异常。我要删除 PIPE_NOWAIT 并尝试 mksteve 的回答。

标签: c# c++ windows multithreading named-pipes


【解决方案1】:

在 C++ 中,您需要创建具有重叠 io 的文件,然后 WaitForMultipleObjects(..., INFINITE); 用于停止事件和 IO。

如果你得到了停止事件,那么你CancelIO();

对于 C#,msdn : Overlapped 允许您创建重叠对象(读取所必需的)。

stackoverflow : pinvoke ReadFile。这显示了如何使用重叠选项本地调用 ReadFile。

stackoverflow : waitformultipleobjects functionality 解释如何调用 WaitForMultipleObjects。

在解决这类问题时,我创建了一个独立函数

ReadFileWithEvent( HANDLE file, VOID * pData, size_t data, HANDLE exitEvent, BOOL & signalled );

其中打包创建了一个重叠对象,等待并向调用者解释发生了停止,并且读取尚未完成。这简化了我需要的代码。

【讨论】:

    【解决方案2】:

    我用PeekNamedPipe() 解决了这个问题,我得到了可用的总字节数,只有当它> 0 时才调用ReadFile()。这是一种模拟非阻塞模式的简单方法,我可以退出循环运行在一个线程中,只是将done 设置为true

    类似这样的:

       while(!done) {
                DWORD total_available_bytes = 0;
                if(!PeekNamedPipe(hPipe2, NULL, 0, NULL, &total_available_bytes, NULL)) {
                    /* error handling goes here */
                    break;
                }
    
                // for avoid overuse of the CPU, sleep for a while until next check.
                if(total_available_bytes == 0) {
                    Sleep(500);
                    continue;
                }
    
               if(ReadFile(hPipe2, ...)) {
                 // ...
               }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多