我有这个奇怪的问题。 我需要从后台工作中调用的过程
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
问题是,在执行等待_tcs.Task当_bwConvertMedia RunWorkerCompleted程序执行,所以当我做致电_bwConvertMedia.ReportProgress(AI)
我得到了工人已经完成了一个错误。
这是为什么? 你能帮助我吗?
什么情况是
- DoWork的 - 迭代1
- 在等待过程1
- RunWorkerComplete
- DoWork的迭代2-100
正确的行为是后台工作调用100次的过程,然后完成执行,并调用RunWorkerCompleted
我提出了一些修改代码我以前链接,这里是一个顺序非阻塞异步/等待过程和使用非阻塞并行程序的两个示例Task.Factory
。
既然不能测试你的计划,我只是用Tracert.exe
模拟标准输出结果更新用户界面。
到正在运行的任务/线程与UI同步,我在第一种情况下所使用的.SynchronizingObject
的处理的以及在所述第二所述的TaskScheduler方法TaskScheduler.FromCurrentSynchronizationContext()
从输出Tracert.exe
传递给两个文本框。 在并行实例,我插入任务之间1秒的延迟,看到两个文本框的更新方式。
异步/等待例如可以进行修改,以不同的方式工作,因为你并不需要等待一个任务完成,开始另一个。
所述ProcessStartInfo
和Process
对象被添加到使用池 List(Of ProcessStartInfo)
和List(Of Process)
。
这些在两个实施例中使用。 定义一个正确的范围。
Public psInfoPool As List(Of ProcessStartInfo)
Public ProcessPool As List(Of Process)
连续异步/等待
委托使用具有SynchronizingObject.BeginInvoke如果InvokeRequired =真
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
参数仅用于输出的文本框
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)
你必须是一个异步方法,但它只是用于测试的输出,它是没有必要的。
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