随笔 - 89, 评论 - 163, 引用 - 33

导航

关于

标签

每月存档

最新留言

广告

 

[原文作者]:Lucian Wischik

[原文链接]:System.Diagnostics.Process: redirect StandardInput, StandardOutput, StandardError

    有时我们运行一个外部适用程序,向里面输入数据然后获取它的输出。这往往很容易发生这样的死锁:

     ' BAD CODE

     Using p As New System.Diagnostics.Process

     p.StartInfo.FileName = "cat"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardInput = True

     p.Start()

     p.StandardInput.Write("world" & vbCrLf & "hello")

    ' 这里将发生死锁,如果P写到输出接口的量达到12k

     p.StandardInput.Close()

     Dim op = p.StandardOutput.ReadToEnd()

     p.WaitForExit()

     p.Close()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     End Using

    这段程序将发生死锁,因为“Cat”首先从标准输入接口中读取,然后写到标准输出接口,最后再读取它,这样一直循环下去直到没有任何东西可读取。但是如果它的标准输出接口已经填满了,并且没有对象去读取,这样就不能继续往里面写东西,从而出现阻塞。

    这里的12k仅是一个随机数,我不会关注于它...

     ' BAD CODE

     Using p As New System.Diagnostics.Process

     p.StartInfo.FileName = "findstr"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardError = True

     p.Start()

    ' deadlock here if p needs to write more than 12k to StandardError

     Dim op = p.StandardOutput.ReadToEnd()

     Dim err = p.StandardError.ReadToEnd()

     p.WaitForExit()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     Console.WriteLine("ERROR:") : Console.WriteLine(err)

     End Using

     MSDN文档中说:你可以通过异步读取操作来避免这些依赖和潜在的死锁;或者你可以通过创建两个线程,让其中一个独立线程来读取输出流来避免死锁。因此我们将这样来做:

     使用线程来重定向就不会死锁

     '  GOOD CODE: 这里不会发生死锁.

     Using p As New Diagnostics.Process

     p.StartInfo.FileName = "sort"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardInput = True

     p.Start()

     Dim op = ""

     ' do NOT WaitForExit yet since that would introduce deadlocks.

      p.InputAndOutputToEnd("world" & vbCrLf & "hello", op, Nothing)

     p.WaitForExit()

     p.Close()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     End Using

     ''' <summary>

     ''' InputAndOutputToEnd: a handy way to use redirected input/output/error on a p.

     ''' </summary>

     ''' <param name="p">The p to redirect. Must have UseShellExecute set to false.</param>

     ''' <param name="StandardInput">This string will be sent as input to the p. (must be Nothing if not StartInfo.RedirectStandardInput)</param>

     ''' <param name="StandardOutput">The p's output will be collected in this ByRef string. (must be Nothing if not StartInfo.RedirectStandardOutput)</param>

     ''' <param name="StandardError">The p's error will be collected in this ByRef string. (must be Nothing if not StartInfo.RedirectStandardError)</param>

     ''' <remarks>This function solves the deadlock problem mentioned at http://msdn.microsoft.com/en-us/library/system.diagnostics.p.standardoutput.aspx</remarks>  

     <Runtime.CompilerServices.Extension()> Sub InputAndOutputToEnd(ByVal p As Diagnostics.Process, ByVal StandardInput As String, ByRef StandardOutput As String, ByRef StandardError As String)

     If p Is Nothing Then Throw New ArgumentException("p must be non-null")

     ' Assume p has started. Alas there's no way to check.

      If p.StartInfo.UseShellExecute Then Throw New ArgumentException("Set StartInfo.UseShellExecute to false")

      If (p.StartInfo.RedirectStandardInput <> (StandardInput IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Input only when StartInfo.RedirectStandardInput")

      If (p.StartInfo.RedirectStandardOutput <> (StandardOutput IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Output only when StartInfo.RedirectStandardOutput")

     If (p.StartInfo.RedirectStandardError <> (StandardError IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Error only when StartInfo.RedirectStandardError")

 

     Dim outputData As New InputAndOutputToEndData

     Dim errorData As New InputAndOutputToEndData

 

     If p.StartInfo.RedirectStandardOutput Then

     outputData.Stream = p.StandardOutput

     outputData.Thread = New Threading.Thread(AddressOf InputAndOutputToEndProc)

     outputData.Thread.Start(outputData)

     End If

     If p.StartInfo.RedirectStandardError Then

     errorData.Stream = p.StandardError

     errorData.Thread = New Threading.Thread(AddressOf InputAndOutputToEndProc)

     errorData.Thread.Start(errorData)

     End If

 

     If p.StartInfo.RedirectStandardInput Then

     p.StandardInput.Write(StandardInput)

     p.StandardInput.Close()

    End If

'

    If p.StartInfo.RedirectStandardOutput Then outputData.Thread.Join() : StandardOutput = outputData.Output

    If p.StartInfo.RedirectStandardError Then errorData.Thread.Join() : StandardError = errorData.Output

    If outputData.Exception IsNot Nothing Then Throw outputData.Exception

    If errorData.Exception IsNot Nothing Then Throw errorData.Exception

    End Sub

    Private Class InputAndOutputToEndData

    Public Thread As Threading.Thread

    Public Stream As IO.StreamReader

    Public Output As String

    Public Exception As Exception

    End Class

    Private Sub InputAndOutputToEndProc(ByVal data_ As Object)

    Dim data = DirectCast(data_, InputAndOutputToEndData)

    Try : data.Output = data.Stream.ReadToEnd : Catch e As Exception : data.Exception = e : End Try

    End Sub

打印 | 张贴于 2009-02-20 14:19:00 | Tag:VB Team Blog  Visual Basic  Debugging

留言反馈

暂时没有留言纪录
博客主人设置本博客不允许匿名用户发表言论,请登录后再试

Powered by: Joycode.MVC引擎 0.5.2.0