我有这个奇怪的问题。我需要从后台工作程序中调用一个进程
Private Shared _process As Process
Private Shared _StartInfo As ProcessStartInfo
Private WithEvents _bwConvertMedia As New BackgroundWorker
以下是DoWorkAsync中的工作
Private Async Sub _bwConvertMedia_DoWorkAsync(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles _bwConvertMedia.DoWork
For AI = 1 To 100
_StartInfo = New ProcessStartInfo(".\mycmd.exe", "-1")
_StartInfo.RedirectStandardOutput = True
_StartInfo.UseShellExecute = False
_StartInfo.CreateNoWindow = True
_StartInfo.RedirectStandardError = True
_process = New Process() With {.EnableRaisingEvents = True, .StartInfo = _StartInfo}
AddHandler _process.OutputDataReceived, AddressOf OutputHandler
AddHandler _process.ErrorDataReceived, AddressOf ErrorHandler
AddHandler _process.Exited, AddressOf Exited
Try
aSuccess = Await AwaitProcess()
Catch ex As Exception
End Try
_bwConvertMedia.ReportProgress(ai)
Next
在这里
Private Shared Async Function AwaitProcess() As Task(Of Integer)
_tcs = New TaskCompletionSource(Of Integer)
_status.Converting = True
_Error.Clear()
_process.Start()
_process.BeginErrorReadLine()
_process.BeginOutputReadLine()
Return Await _tcs.Task
End Function
问题是,当执行Await _tcs.Task时,会执行_bwConvertMedia RunWorkerCompleted过程,所以当我调用_bwConvertMedia.ReportProgress(ai)时
我收到工人已经完成的错误。
为什么?你能救我吗?
会发生什么
正确的行为是后台工作程序调用该过程100次,然后它完成执行并调用RunWorkerCompleted
答案 0 :(得分:1)
我对之前链接的代码进行了一些修改,这里是使用Task.Factory
的顺序非阻塞Async / Await过程和非阻塞并行过程的两个示例。
由于我无法测试您的程序,因此我只使用了
Tracert.exe
模拟stdout结果以更新用户界面。
为了使正在运行的任务/线程与UI同步,我在第一种情况下使用了进程的.SynchronizingObject
,在第二种情况下使用了TaskScheduler方法TaskScheduler.FromCurrentSynchronizationContext()
。
Tracert.exe
的输出传递给两个TextBox。
在并行示例中,我在Tasks之间插入了1秒的延迟,以查看两个TextBox如何更新。
Async / Await示例可以修改为以不同方式工作,因为您无需等待任务完成以启动另一个任务。
使用ProcessStartInfo
和Process
将List(Of ProcessStartInfo)
和List(Of Process)
个对象添加到池。
这两个例子都使用了这些。定义正确的范围。
Public psInfoPool As List(Of ProcessStartInfo)
Public ProcessPool As List(Of Process)
顺序异步/等待
委托与SynchronizingObject.BeginInvoke if一起使用 InvokeRequired = true
Public Delegate Sub UpdUI(_object As TextBox, _value As String)
Public Sub UpdateUIDelegate(control As TextBox, _input As String)
control.AppendText(_input)
End Sub
Dim NumberOfProcesses As Integer
For x = 0 To 1
Dim OutCtl As TextBox = If(x = 0, Me.TextBox1, Me.TextBox2)
Dim _result As Integer = Await Task.Run(Async Function() As Task(Of Integer)
Return Await Test_SequentialAsync("192.168.1.1", OutCtl)
End Function)
NumberOfProcesses += _result
Next
MediaToConvert
参数将是文件的名称 根据需要调整示例时进行转换。OutCtl
参数只是用于输出的TextBox
Public Async Function Test_SequentialAsync(ByVal MediaToConvert As String, OutCtl As TextBox) As Task(Of Integer)
Dim _CurrentProcessInfo As Integer
Dim _CurrentProcess As Integer
Dim ExitCode As Integer = Await Task.Run(Function() As Integer
Dim _processexitcode As Integer
psInfoPool.Add(New ProcessStartInfo)
_CurrentProcessInfo = psInfoPool.Count - 1
psInfoPool(_CurrentProcessInfo).RedirectStandardOutput = True
psInfoPool(_CurrentProcessInfo).CreateNoWindow = True
psInfoPool(_CurrentProcessInfo).UseShellExecute = False
'Name of the executable to start
psInfoPool(_CurrentProcessInfo).FileName = "Tracert" 'psInfo.FileName = ".\mycmd.exe"""
'Parameter(s) to pass to the executable
psInfoPool(_CurrentProcessInfo).Arguments = MediaToConvert
psInfoPool(_CurrentProcessInfo).WindowStyle = ProcessWindowStyle.Hidden
ProcessPool.Add(New Process)
_CurrentProcess = ProcessPool.Count - 1
ProcessPool(_CurrentProcess) = New Process() With {.StartInfo = psInfoPool(_CurrentProcessInfo),
.EnableRaisingEvents = True,
.SynchronizingObject = Me}
ProcessPool(_CurrentProcess).Start()
ProcessPool(_CurrentProcess).BeginOutputReadLine()
AddHandler ProcessPool(_CurrentProcess).OutputDataReceived,
Sub(sender As Object, e As DataReceivedEventArgs)
If e.Data IsNot Nothing Then
If ProcessPool(_CurrentProcess).SynchronizingObject.InvokeRequired Then
ProcessPool(_CurrentProcess).SynchronizingObject.BeginInvoke(
New UpdUI(AddressOf UpdateUIDelegate),
New Object() {OutCtl,
e.Data + Environment.NewLine})
Else
OutCtl.AppendText(e.Data + Environment.NewLine)
End If
End If
End Sub
'Add an event handler for the Exited event
AddHandler ProcessPool(_CurrentProcess).Exited,
Sub(source As Object, ev As EventArgs)
_processexitcode = ProcessPool(_CurrentProcess).ExitCode
Console.WriteLine("The process has exited. Code: {0} Time: {1}",
_processexitcode,
ProcessPool(_CurrentProcess).ExitTime)
End Sub
ProcessPool(_CurrentProcess).WaitForExit()
ProcessPool(_CurrentProcess).Close()
Return _processexitcode
End Function)
Return If(ExitCode = 0, 1, 0)
End Function
使用Task.Fatory的并行进程
定义计划程序并将其与当前上下文相关联
Public _Scheduler As TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
要使用
Await Task.Delay(1000)
,您必须使用Async方法,但是 它只是用于测试输出,不需要它。
For x = 0 To 1
Dim OutCtl As TextBox = If(x = 0, Me.TextBox1, Me.TextBox2)
Dim _result As Integer = Test_ParallelTasks("192.168.1.1", OutCtl)
Await Task.Delay(1000)
NumberOfProcesses += _result
Next
请注意,
OutputDataReceived
事件时会创建一个新任务 处理程序报告已收到新数据。用户界面已更新 相应地使用DataReceivedEventArgs
e.Data。
Private Function Test_ParallelTasks(ByVal MediaToConvert As String, OutCtl As TextBox) As Integer
Dim _processexitcode As Integer
Dim _CurrentProcessInfo As Integer
Dim _CurrentProcess As Integer
Task.Factory.StartNew(Function()
psInfoPool.Add(New ProcessStartInfo)
_CurrentProcessInfo = psInfoPool.Count - 1
psInfoPool(_CurrentProcessInfo).RedirectStandardOutput = True
psInfoPool(_CurrentProcessInfo).CreateNoWindow = True
psInfoPool(_CurrentProcessInfo).UseShellExecute = False
psInfoPool(_CurrentProcessInfo).FileName = "Tracert" 'psInfo.FileName = ".\mycmd.exe"
psInfoPool(_CurrentProcessInfo).Arguments = MediaToConvert
psInfoPool(_CurrentProcessInfo).WindowStyle = ProcessWindowStyle.Hidden
ProcessPool.Add(New Process)
_CurrentProcess = ProcessPool.Count - 1
ProcessPool(_CurrentProcess) = New Process() With {.StartInfo = psInfoPool(_CurrentProcessInfo),
.EnableRaisingEvents = True,
.SynchronizingObject = Me}
ProcessPool(_CurrentProcess).Start()
ProcessPool(_CurrentProcess).BeginOutputReadLine()
AddHandler ProcessPool(_CurrentProcess).OutputDataReceived,
Sub(sender As Object, e As DataReceivedEventArgs)
If e.Data IsNot Nothing Then
Try
'Update the UI or report progress
Dim UpdateUI As Task = Task.Factory.StartNew(Sub()
Try
OutCtl.AppendText(e.Data + Environment.NewLine)
Catch exp As Exception
'An exception may raise if the form is closed
End Try
End Sub, CancellationToken.None, TaskCreationOptions.PreferFairness, _Scheduler)
UpdateUI.Wait()
Catch exp As Exception
'Do something here
End Try
End If
End Sub
'Add an event handler for the Exited event
AddHandler ProcessPool(_CurrentProcess).Exited,
Sub(source As Object, ev As EventArgs)
_processexitcode = ProcessPool(_CurrentProcess).ExitCode
Console.WriteLine("The process has exited. Code: {0} Time: {1}",
_processexitcode,
ProcessPool(_CurrentProcess).ExitTime)
End Sub
ProcessPool(_CurrentProcess).WaitForExit()
ProcessPool(_CurrentProcess).Close()
Return _processexitcode
End Function, TaskCreationOptions.LongRunning, CancellationToken.None)
Return If(_processexitcode = 0, 1, 0)
End Function