使用PowerShell多线程比使用单线程脚本减少输出

时间:2014-08-25 15:43:04

标签: multithreading powershell

我在Windows 7桌面上使用PowerShell 2.0。我试图在企业CIFS股票中搜索关键字/正则表达式。我已经有一个简单的单线程脚本可以执行此操作,但单个关键字需要19-22小时。我已经根据Surly Admin的文章创建了一个多线程脚本,这是多线程的第一次尝试。

Can Powershell Run Commands in Parallel?

Powershell Throttle Multi thread jobs via job completion

以及与这些帖子相关的链接。

我决定使用运行空间而不是后台作业,因为流行的智慧说这更有效率。问题是,我是否只使用我所拥有的多线程脚本获得部分结果输出。不确定它是I / O的东西还是记忆的东西,或其他东西。希望有人可以提供帮助。这是代码。

cls
Get-Date
Remove-Item C:\Users\user\Desktop\results.txt

$Throttle = 5 #threads

$ScriptBlock = {
    Param (
        $File
    )
    $KeywordInfo = Select-String -pattern KEYWORD -AllMatches -InputObject $File
    $KeywordOut = New-Object PSObject -Property @{
        Matches = $KeywordInfo.Matches
        Path = $KeywordInfo.Path
    }
    Return $KeywordOut
}

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = @()

$Files = Get-ChildItem -recurse -erroraction silentlycontinue
ForEach ($File in $Files) {
    $Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($File)
    $Job.RunspacePool = $RunspacePool
    $Jobs += New-Object PSObject -Property @{
        File = $File
        Pipe = $Job
        Result = $Job.BeginInvoke()
    }
}

Write-Host "Waiting.." -NoNewline
Do {
    Write-Host "." -NoNewline
    Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host "All jobs completed!"

$Results = @()
ForEach ($Job in $Jobs) {
    $Results += $Job.Pipe.EndInvoke($Job.Result)
    $Job.Pipe.EndInvoke($Job.Result) | Where {$_.Path} | Format-List | Out-File -FilePath C:\Users\user\Desktop\results.txt -Append -Encoding UTF8 -Width 512
}

Invoke-Item C:\Users\user\Desktop\results.txt
Get-Date

这是我正在使用的单线程版本,包括我用于社交的正则表达式。

cls
Get-Date

Remove-Item C:\Users\user\Desktop\results.txt

$files = Get-ChildItem -recurse -erroraction silentlycontinue

ForEach ($file in $files) {
    Select-String -pattern '[sS][sS][nN]:*\s*\d{3}-*\d{2}-*\d{4}' -AllMatches -InputObject $file | Select-Object matches, path |
        Format-List | Out-File -FilePath C:\Users\user\Desktop\results.tx -Append -Encoding UTF8 -Width 512
}

Get-Date
Invoke-Item C:\Users\user\Desktop\results.txt

2 个答案:

答案 0 :(得分:0)

我希望随着时间的推移建立这个答案,因为我不想评论。我不知道为什么你丢失多线程数据,但我认为我们可以通过更新的正则表达式来提高性能。对于初学者来说,你有许多贪婪的量词,我认为我们可以缩减它们。

[sS][sS][nN]:*\s*\d{3}-*\d{2}-*\d{4}

Select-String默认情况下不区分大小写,因此您不需要开头的部分。你需要检查多个冒号吗?因为你要找0或多:。连字符也是如此。也许这些会更好吗?匹配0或1。

ssn:?\s*\d{3}-?\d{2}-?\d{4}

这假设您正在寻找大多数正确格式化的SSN。如果人们将它们隐藏在文本中,您可能也需要寻找其他分隔符。

我还建议将文本添加到单独的文件中,并可能在执行后将它们组合在一起。如果没别的只是为了测试。

希望这将成为正确解决方案的开始。

答案 1 :(得分:0)

事实证明,由于某种原因,Select-String cmdlet在多线程方面遇到了问题。我没有足够的开发人员背景能够分辨出幕后发生的事情。但是我确实发现通过在Select-String中使用-quiet选项,将其转换为布尔输出,我能够得到我想要的结果。

每个文档中的第一个模式匹配给出了真实值。当我得到一个真实然后我将文档的路径返回到一个数组。完成后,我将对从scriptblock输出的路径运行模式匹配。这并不像我希望的那样有效,但仍然比单线程有了相当大的改进。

我遇到的另一个问题是通过尝试在每个阶段将结果输出到文档来读取/写入磁盘。我把它改成了数组。虽然内存密集,但速度要快得多。

以下是生成的代码。关于性能改进的任何其他提示都表示赞赏:

cls
Remove-Item C:\Users\user\Desktop\output.txt

$Throttle = 5 #threads

$ScriptBlock = {
   Param (
      $File
   )
   $Match = Select-String -pattern 'ssn:?\s*\d{3}-?\d{2}-?\d{4}' -Quiet -InputObject $File
   if ( $Match -eq $true ) {
        $MatchObjects = Select-Object -InputObject $File
        $MatchOut = New-Object PSObject -Property @{
            Path = $MatchObjects.FullName
        }
   }
   Return $MatchOut
}

$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = @()

$Files = Get-ChildItem -Path I:\ -recurse -erroraction silentlycontinue
ForEach ($File in $Files) {
   $Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($File)
   $Job.RunspacePool = $RunspacePool
   $Jobs += New-Object PSObject -Property @{
      File = $File
      Pipe = $Job
      Result = $Job.BeginInvoke()
   }
}

$Results = @()
ForEach ($Job in $Jobs) {
    $Results += $Job.Pipe.EndInvoke($Job.Result)
}

$PathValue = @()
ForEach ($Line in $Results) {
    $PathValue += $Line.psobject.properties | % {$_.Value}
}

$UniqValues = $PathValue  | sort | Get-Unique

$Output = ForEach ( $Path in $UniqValues ) {
    Select-String -Pattern '\d{3}-?\d{2}-?\d{4}' -AllMatches -Path $Path | Select-Object -Property Matches, Path
}

$Output | Out-File -FilePath C:\Users\user\Desktop\output.txt -Append -Encoding UTF8 -Width 512

Invoke-Item C:\Users\user\Desktop\output.txt