.NET 4.8并行任务中实时读取含CR的进程标准输出问题
解决仅用CR回车符输出的进程实时读取问题(.NET 4.8)
原有的并行进程代码依赖OutputDataReceived事件读取输出,但这个事件只在检测到换行符(LF)时才触发。你的后台程序输出进度时只用回车符(CR)覆盖当前行(比如输出1%\r2%\r...),没有换行,导致事件无法实时触发,只能等进程结束后拿到最终结果。
下面是基于原有Task Factory方案修改后的代码,实现逐字符读取输出、实时响应CR更新UI进度:
Private Function Test_ParallelTasks(ByVal MediaToConvert As String, OutCtl As TextBox) As Integer Dim _processexitcode As Integer = -1 Dim _CurrentProcess As Process = Nothing ' 启动后台任务处理进程 Task.Factory.StartNew(Function() Dim psInfo As New ProcessStartInfo() With { .RedirectStandardOutput = True, .CreateNoWindow = True, .UseShellExecute = False, .FileName = "你的后台程序路径", ' 替换成你的后台程序 .Arguments = MediaToConvert, .WindowStyle = ProcessWindowStyle.Hidden } _CurrentProcess = New Process() With { .StartInfo = psInfo, .EnableRaisingEvents = True, .SynchronizingObject = Me } ' 添加进程退出事件 AddHandler _CurrentProcess.Exited, Sub(source As Object, ev As EventArgs) _processexitcode = _CurrentProcess.ExitCode Console.WriteLine("进程退出,代码: {0} 时间: {1}", _processexitcode, _CurrentProcess.ExitTime) End Sub _CurrentProcess.Start() ' 直接读取StandardOutput流,逐字符处理CR Dim outputBuffer As New StringBuilder() Dim reader As StreamReader = _CurrentProcess.StandardOutput Dim charRead As Integer Dim currentChar As Char While _CurrentProcess.HasExited = False charRead = reader.Read() If charRead = -1 Then ' 流暂时无数据,等待10ms避免空循环占用CPU Thread.Sleep(10) Continue While End If currentChar = Convert.ToChar(charRead) If currentChar = vbCr Then ' 遇到回车符,更新UI并清空缓冲区 Dim progressText As String = outputBuffer.ToString().Trim() If Not String.IsNullOrEmpty(progressText) Then ' 调度到UI线程更新 Task.Factory.StartNew(Sub() Try ' 覆盖当前行:根据需求调整,比如直接替换文本 OutCtl.Text = progressText ' 若需保留历史并覆盖当前行,可改用: ' OutCtl.SelectionStart = 0 ' OutCtl.SelectionLength = OutCtl.Text.Length ' OutCtl.SelectedText = progressText Catch ex As Exception ' 窗体关闭时可能抛出异常,忽略即可 End Try End Sub, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.FromCurrentSynchronizationContext()) End If outputBuffer.Clear() Else outputBuffer.Append(currentChar) End If End While ' 处理进程退出后剩余的输出 If outputBuffer.Length > 0 Then Dim finalText As String = outputBuffer.ToString().Trim() Task.Factory.StartNew(Sub() Try OutCtl.AppendText(finalText + Environment.NewLine) Catch ex As Exception End Try End Sub, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.FromCurrentSynchronizationContext()) End If _CurrentProcess.Close() Return _processexitcode End Function, TaskCreationOptions.LongRunning, CancellationToken.None).Wait() ' 按需决定是否等待后台任务完成 Return If(_processexitcode = 0, 1, 0) End Function
关键修改说明
- 放弃事件驱动读取:不再依赖
BeginOutputReadLine(),改为直接读取StandardOutput流,逐字符解析,摆脱换行符限制。 - 实时响应CR:遇到回车符(
vbCr)立即更新UI并清空缓冲区,模拟后台程序覆盖当前行的行为。 - UI线程安全:通过
TaskScheduler.FromCurrentSynchronizationContext()确保UI更新在主线程执行,避免跨线程异常。 - 优化循环效率:流无数据时短暂休眠,避免空循环占用过多CPU。
- 收尾剩余输出:进程退出后,处理缓冲区中未触发CR的剩余内容。
注意事项
- 若后台程序存在标准输出缓冲(部分程序会批量输出),可能导致CR无法实时传递,此时需修改后台程序禁用缓冲(比如C++程序用
setbuf(stdout, NULL))。 - 可根据需求调整UI更新逻辑,比如保留历史进度或仅显示当前百分比。
内容的提问来源于stack exchange,提问作者Dude named Ben
相关产品推荐
相关产品推荐

