Board logo

标题: [问题求助] PowerShell多线程runspace怎么输出到控制台? [打印本页]

作者: 2198114498    时间: 2025-1-13 15:38     标题: PowerShell多线程runspace怎么输出到控制台?

很奇怪,我又改了两版,暂时不能复现了,其中之一应该是之前的问题代码,但当时没保存,除了Job类忘了改过哪了。
1、这版添了几行输出就没再死锁?!
  1. class Job {
  2.     Init() {
  3.         $this.ps.Streams.Information.add_DataAdded( {
  4.             Param ( [Object]$sender, [System.Management.Automation.DataAddedEventArgs]$e )
  5.     $i=0
  6.             while(!($all = $sender.ReadAll())) {$i++;Write-Warning [Information] ReadAll阻塞:$i;Start-Sleep -Milliseconds 10}
  7.             foreach($o in $all) { Write-Host $o }
  8.         })
  9.     }
  10. }
复制代码
2、这是之前的版本,当时出死锁问题,
  1. class Job {
  2.     Init() {
  3.         $this.ps.Streams.Information.add_DataAdded( {
  4.             Param ( [Object]$sender, [System.Management.Automation.DataAddedEventArgs]$e )
  5.             foreach($o in $sender.ReadAll()) { Write-Host $o }
  6.         })
  7.     }
  8. }
复制代码
在官网查到
https://learn.microsoft.com/zh-c ... powershellsdk-1.1.0
我理解是遇到阻塞返回空,就添加了while代码


原问题:
想实现多线程,搜到推荐runspace,试了试AI给的代码,只能运行完才能看输出,
搜到一种方法,自己改了改,虽然能实时看到输出了,但是大概率会出现线程代码不返回,
怀疑是多线程死锁,但是没有能力调试和解决,请大神帮忙看看,先谢了!

1、注释71行代码,就不能实时输出了,但是不会死锁。。。
2、偶尔还出现过报错:
不能对 Null 值表达式调用方法。
所在位置 :56 字符: 5
+     $RunspacePool.Close()

所在位置 :37 字符: 16
+         while ($Jobs.IsCompleted() -contains $false) {
+                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: ( [],RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
  1. cls
  2. # 处理任务
  3. $Worker = {
  4.     param($Filename)
  5.     $i=0
  6.     Write-Host "Processing $filename"
  7.     $i=1
  8.     #[Threading.Thread]::Sleep(100)
  9.     Start-Sleep -Seconds 1 # Doing some work....
  10.     $i=2
  11.     Write-Host "Processed $filename"
  12.     $i=3
  13. }
  14. # 最大并发
  15. $MaxRunspaces = 10
  16. try {
  17. # 线程池
  18.     $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxRunspaces)
  19.     $RunspacePool.Open()
  20.     $Filenames = @("file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt", "file6.txt", "file7.txt", "file8.txt", "file9.txt", "file10.txt", "file11.txt")
  21.     while($true) {
  22.         # 开始时间
  23.         $StartTime = Get-Date
  24.         $Jobs = New-Object System.Collections.ArrayList
  25.         foreach ($File in $Filenames) {
  26.             Write-Host "Creating runspace for $File"
  27.             $job=[Job]::new($RunspacePool)
  28.             $null=$job.GetPowerShell().AddScript($Worker).AddArgument($File)
  29.             $null=$Jobs.Add($job)
  30.             $job.Run()
  31.         }
  32.         while ($Jobs.IsCompleted() -contains $false) {
  33.             Write-Host (Get-date).Tostring() "Still running..."
  34.             #[Threading.Thread]::Sleep(1000)
  35.             Start-Sleep 1
  36.             $Jobs.IsCompleted()
  37.         }
  38.         # 结束时间
  39.         $endTime = Get-Date
  40.         # 运行时间
  41.         Write-Host "The script runs for Total time (s):$(($endTime - $StartTime).TotalSeconds)"
  42.         # 任务对象信息
  43.         $Jobs.GetPowerShell().Streams.Information
  44.         $Jobs.GetPowerShell().haderrors
  45.         $c=Read-Host '输入e退出'
  46.         if($c -eq 'e') {break}
  47.     }
  48. }
  49. finally {
  50. # 始终关闭进程池
  51.     $RunspacePool.Close()
  52. }
  53. class Job {
  54.     hidden [powershell]$ps = [powershell]::Create()
  55.     hidden [IAsyncResult]$result
  56.    
  57.     Job([System.Management.Automation.Runspaces.RunspacePool]$runspacepool) {
  58.         $this.ps.RunspacePool = $runspacepool
  59.         $this.Init()
  60.     }
  61.     Init() {
  62.         $this.ps.Streams.Information.add_DataAdded( {
  63.             Param ( [Object]$sender, [System.Management.Automation.DataAddedEventArgs]$e )
  64.             while(!($all = $sender.ReadAll())) {Start-Sleep -Milliseconds 10}
  65.             foreach($o in $all) { Write-Host $o }
  66.         })
  67.     }
  68.     [powershell] GetPowerShell() {
  69.         return $this.ps
  70.     }
  71.    
  72.     [IAsyncResult] GetResult() {
  73.         return $this.result
  74.     }
  75.     [bool]IsCompleted() {
  76.         return $this.result.IsCompleted
  77.     }
  78.     [IAsyncResult] Run() {
  79.         return $this.result = $this.ps.BeginInvoke()
  80.     }
  81. }
复制代码

作者: flashercs    时间: 2025-1-13 16:15

本帖最后由 flashercs 于 2025-1-13 16:17 编辑

powershell 有个简单的多线程方法是 workflow,但是workflow不支持完整的powershell features,且不支持pwsh 6+,例如:powershell 5.1
不支持Write-Host
  1. workflow work1 {
  2.     $Filenames = @("file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt", "file6.txt", "file7.txt", "file8.txt", "file9.txt", "file10.txt", "file11.txt")
  3.   # workflow有很多限制
  4.   # foreach -parallel允许同时最多开启5个线程
  5.   foreach -parallel ($Filename in $Filenames) {
  6.     $i=0
  7.      "Processing $filename"
  8.     $i=1
  9.     #[Threading.Thread]::Sleep(100)
  10.     Start-Sleep -Seconds 1 # Doing some work....
  11.     $i=2
  12.      "Processed $filename"
  13.     $i=3
  14.   }
  15.   
  16. }
  17. [System.Diagnostics.Stopwatch]$watch = [System.Diagnostics.Stopwatch]::StartNew()
  18. work1
  19. $watch.Stop()
  20. $watch.Elapsed # 耗时2.21秒
复制代码

作者: 2198114498    时间: 2025-1-13 17:05

回复 2# flashercs


    非常感谢!使用Win10自带应该是5.1,目前还在试验多线程就遇到不少问题,预想的功能还没添加。
我原想是控制台输入路径,开一个主线程读取文件,然后将字符串共享给多个处理线程,
然后主线程继续读取下一个文件,处理线程将结果输出到控制台。
不知workflow能实现吗
作者: Five66    时间: 2025-1-13 18:49

直接就写在一起啦 , 看得脑阔疼
  1. #Runspace最大数
  2. $MaxRunspaces = 10
  3. #Runspace池
  4. $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxRunspaces)
  5. $RunspacePool.Open()
  6. #任务
  7. $Worker = {
  8. param($Filename)
  9. if(test-path $Filename){
  10. $all = gc $Filename
  11. foreach($o in $all) { [console]::writeline($o)}
  12. $rt=0
  13. }else{
  14. $rt=-1
  15. }
  16. $rt
  17. }
  18. $Filenames = @("file1.txt", "file2.txt", "file3.txt", "file4.txt", "file5.txt", "file6.txt", "file7.txt", "file8.txt", "file9.txt", "file10.txt", "file11.txt")
  19. $jobs = @()
  20. #添加并执行任务
  21. foreach ($File in $Filenames) {
  22. $ps = [Powershell]::Create()
  23. $job = $ps.AddScript($Worker).AddArgument($File)
  24. $job.RunspacePool = $RunspacePool
  25. $jobs += New-Object PSObject -Property @{
  26. Job = $job
  27. PowerShell = $ps
  28. Result = $job.BeginInvoke()
  29. }
  30. }
  31. #判断任务是否完成
  32. do{
  33. Start-Sleep -Milliseconds 500
  34. $c = ($jobs | Where {$_.Result.IsCompleted -ne $true}).Count
  35. Write-Host ("未完成任务数" + $c) -ForegroundColor Green
  36. }while($c -gt 0)
  37. #获取返回结果并释放资源
  38. $i=1;foreach($j in $jobs){
  39. $r = $j.Job.EndInvoke($j.Result)
  40. write-host "任务 $i 返回值 : $r" -ForegroundColor Yellow
  41. $j.PowerShell.Dispose()
  42. $i+=1
  43. }
  44. cmd /c pause
复制代码
没啥特别需求可以用多进程的start-job




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2