.net - Console.In.ReadLine(重定向的StdIn)卡住所有其他线程

标签 .net vb.net multithreading pipe

我正在使用管道将旧应用程序(用COBOL编写)与我们的.NET新应用程序连接。
这个想法很简单:旧版程序(我的ERP菜单)在流上写入一些参数,.NET应用通过Console.In流读取它,并启动一个新线程以打开请求的屏幕。这是.NET方面的想法摘要:

<STAThread(), LoaderOptimization(LoaderOptimization.MultiDomain)>
Shared Sub Main()
    If Environment.GetCommandLineArgs(1) = "PIPE"
       While True
          Dim pipeParam as String
          Try
             pipeParam = Console.In.ReadLine()
          Catch e as Exception
             Exit Sub
          End Try

          ' Deal with parameters here

          If pipeParam = "END"
             Dim newThread as New Threading.Thread(Sub()
                                                       ' Create a new AppDomain and Loads the menu option, generally a Winforms form.
                                                   End Sub)
             newThread.Start()                 
          End If
       End While

    End If
End Sub

一切都很好,很容易...直到今天。
我在客户端环境(Windows Server 2003)中部署了该解决方案,并且碰巧没有执行所请求的线程,除非终止了被调用的进程(COBOL)(即,Console.In被强制关闭)。从那时起,所有请求的winform将开始显示并按预期方式运行。

用日志挖掘这种奇怪的行为,我发现线程一直在正常执行,直到执行了IDictionary.ContainsKey()语句(或其他需要执行 native 代码的方法)为止。此时,线程处于冻结/休眠状态。

如果我将线程创建限制为三个,那么碰到的是,每个创建的线程都将挂起,直到第三个线程挂起,即不再执行Console.In.ReadLine

我应该怎么做?有什么建议吗?

更多信息:
到目前为止,我找到的最接近的方向是汉斯·帕森特(Hans Passant)在这个问题上的答案:
Interface freezes in multi-threaded c# application(.NET SystemEvents恰巧出现在我的调试器线程列表中,但使用建议的解决方案无法解决我的问题)。

更新新闻

我可以通过等待子线程完成Form的加载来解决此问题。该“Is Ready”信号通过AppDomain.SetData()AppDomain.GetData()传递。以某种方式,在表单创建之后,当主线程进入Console.ReadLine时,子线程不再冻结。尽管问题已解决,但我对此很感兴趣。我试图在“尽可能简单”的测试用例中重现这一点。

一些更多详细信息
  • 入口点.exe被编译为32位。其他所有库均为“AnyCpu”。此问题发生在32位(我的客户端)和64位(我的开发)计算机(都为Windows Server 2003)上运行。
  • 更新了以上片段中的Sub Main()属性。
  • 尝试将Console.ReadLine放入工作线程中。未解决(请参见下图)。
  • COBOL应用程序不会冻结,因为它是在单独的OS进程中执行的。管道恰好是我的IPC方法。在这种情况下,COBOL应用程序仅写入参数,而无需等待响应。
  • 堆栈跟踪在下面的图像中(线程PRX001235在连接到数据库之前和有效加载表单之前对XML配置文件进行反序列化-在这种情况下,似乎仍处于托管代码中-有时PRX001235线程将冻结在其中尝试连接到数据库时的 native 代码):
  • 最佳答案

    首先,您正在做非常不寻常的事情,因此不寻常的结果并不意外。其次,您要执行的操作严格是Windows SDK文档中的verboten。这表明创建单线程单元的线程决不允许进行阻塞调用。

    显然,您的工作线程被锁定在操作系统肠道内部某处的锁上,该锁由程序的主线程获取。看到那些被阻塞线程之一的调用堆栈来识别可能的锁定会很有帮助。这需要启用非托管代码调试,以便您可以查看非托管堆栈框架并启用Microsoft Symbol Server,以便获取Windows代码的调试符号,并可以从堆栈跟踪中了解信息。

    没有人看,我不得不推测:

  • Console.ReadLine()本身具有内部锁定,该锁定可使控制台安全地从多个线程使用,从而序列化对Read()的调用。任何使用Console方法的线程都可以打相同的锁。这不太可能是罪魁祸首,不太可能这些工作线程也在使用控制台。
  • 严格的锯齿角度与单元线程(COM功能)的相关性有关。通过程序的Main()方法的[STAThread]属性或Thread.SetApartmentState()启用STA。 STA必须使用基本上是线程不安全的组件,例如窗口,剪贴板,拖放,shell对话框(如OpenFileDialog)和许多其他COM协同类,其中一些可能由于它们被.NET类包装而无法识别。 。 STA公寓可确保以线程安全的方式使用这些组件,从工作线程对该组件的方法的调用将自动编码到创建该组件的线程中。与Control.Invoke()完全等效。这种编码(marshal)处理要求线程泵送一个消息循环,以将调用分派(dispatch)到正确的线程上。当您的主线程在Console.ReadLine()调用中被阻止时,它的主线程是而不是抽水。工作线程将在调用中停止,直到主线程再次开始泵送。死锁的可能性很高,尽管您实际上并没有完全死锁,因为ReadLine()调用最终会完成。值得注意的是,CLR避免了此类死锁,当您使用lock关键字,在同步对象上调用WaitOne()或调用Thread.Join()时(它是.NET编程中的常见阻塞调用),它将清除消息循环。但是,它不会对Console.ReadLine()执行此操作,至少直到Mark所示的.NET 4.0解决方法为止。
  • 具有高度投机性,它是由您观察到通过等待创建表单来避免问题而观察到的。您可能在64位操作系统上遇到此问题,并且您的EXE项目的“平台”目标设置设置为“x86”,而不是“AnyCPU”。在这种情况下,您的程序将在WOW64仿真层中运行,该层允许64位操作系统执行32位程序。 Windows窗口管理器是一个64位子系统,通过调用它来将通知发送到32位窗口,并通过一个切换位大小的thunk进行调用。窗体管理器传递WM_SHOWWINDOW消息时触发的Form.Load事件存在问题。这会使仿真层处于困难状态,因为它会多次穿越64位到32位边界。这也是this answer中记录的一个非常令人讨厌的异常吞咽问题的根源。在Load事件触发时,此代码持有仿真器锁定的可能性非常高。因此,在Load事件中调用Console.ReadLine()可能很麻烦,我希望任何32位辅助线程在进行api调用时也会传递此锁。如果可以将Platform目标更改为AnyCPU,则高度推测,但易于测试。

  • 鉴于解决方案非常简单,因此不确定是否值得追究此问题的原因。只是不要在主线程上调用Console.ReadLine()。而是在辅助线程上调用它。当COBOL程序无响应时,这也可以防止UI冻结。但是请注意,如果您收到的任何内容也会更新您的UI,则现在必须使用Control.Begin/Invoke()进行编码。

    关于.net - Console.In.ReadLine(重定向的StdIn)卡住所有其他线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13437171/

    相关文章:

    c++ - 用自己的版本替换 std::async 但 std::promise 应该住在哪里?

    python - "generate "多个 TCP 客户端如何使用 Threads 而不是打开多个终端实例并多次运行脚本?

    c# - 发布参数始终为空

    c# - 使用 STA 时表单表现异常,线程耗时过长

    vb.net - 使用 GetField 调用Member。在 VB.NET 中找不到字段

    c# - SynchronizationContext 和 TaskScheduler 之间的概念区别是什么

    .net - WCF 请求拦截器 - 不再支持?

    java - 在 java 中像在 .NET 中一样使用 lapack 库

    json - VB.NET 是否可以将 JSON 数据导出到 JSON 格式的文件中?

    .net - 调试 ClickOnce 部署的 VB.NET 应用程序