用于提高状态行性能的 Powershell 作业

Posted

技术标签:

【中文标题】用于提高状态行性能的 Powershell 作业【英文标题】:Powershell jobs to increase statusline performance 【发布时间】:2019-09-10 02:05:36 【问题描述】:

我正在开发一个自定义的 powershell 状态行,并实现了一些很酷的功能:电池监控、wifi 监控等。不幸的是,这些更新往往很慢,因为它们需要调用 Get-NetAdapterStatistics、获取 wmi 对象、和其他非常慢的电话。我试图通过 start-job 生成子进程以允许后台轮询和更新共享变量,但无法弄清楚如何做到这一点。我找到的最好的替代品是一种 IPC(如此处所述:Pipelining between two SEPARATE Powershell processes),但如果可能的话,我更愿意坚持使用传统的共享变量。有没有办法做到这一点?我发现的最佳选择是将变量作为参数传递,但这不允许轮询。

声明,我知道这并不是 powershell 的真正用途,但我仍然想知道这是否可能。或者是编写返回状态行的 c/c++ 二进制文件的最佳选择?写入文件(这看起来可能很慢)?如果您有能力,或者您需要更多信息,请告诉我什么可行。谢谢你。

更多信息:

Start-Job -Name testJob -Script 
        $testVar = "asdf"


Write-Host $testVar
# should output asdf

有没有办法做到这一点?我正在尝试做一些工作并返回一个变量。这怎么可能?我发现的唯一可能的方法是:

    写文件到磁盘,有点慢

    使用 IPC 管道:

$pipe = New-Object System.IO.Pipes.NamedPipeClientStream '.',"testPipe",'In'
Start-Job -Name testJob -Script 
        $pipe = New-Object System.IO.Pipes.NamedPipeServerStream "testPipe",'Out'
        $pipe.WaitForConnection()
        $sw = New-Object System.IO.StreamWriter $pipe
        $sw.AutoFlush = $true
        $sw.writeLine("foo")
        While ($true) 
                # do looping and polling, then print stuff
                $sw.writeLine($pollResult)
        
        $sw.Dispose()
        $pipe.Dispose()


$pipe.Connect()
$sr = New-Object System.IO.StreamReader $pipe
#when necessary, read info
$output = $sr.ReadLine()
$sr.Dispose()
$pipe.Dispose()

最大的缺点是它有点小故障。最重要的是,当我关闭 powershell 窗口(因为这是一个状态行)时,我不知道如何关闭 IPC 管道,我最终会出现“管道泄漏”,导致 CPU 使用率高,并且 powershell 进程正在运行背景。这至少不是直接后台作业写入文件的情况。显然,当最后一个引用被删除时,管道应该关闭,但后台作业会在它打开的情况下继续运行。这是因为 powershell 会话在等待与管道相关的内容时(等待管道连接、完成写入一行等)时将挂起到无法按 ctrl-c'd 的程度。

谢谢,如果我可以添加更多信息,请告诉我。

更新:我尝试使用空文件基本上作为控制标志(我认为这比为每个设置解析一个文件要快),但我试图使用作业来返回 VCS 信息(这更容易返回,并且更快)。关于如何解决这个问题的任何想法?我难住了。

注意:我标记了这个 C#,因为 powershell 使用 C# 的管道函数,我希望有这方面知识的人可以提供帮助。

【问题讨论】:

请展示您正在寻找的内容的示例,即使它只是伪代码。 我试图弄清楚我是否可以在 powershell 作业之间共享一个变量。我在上面添加了更多细节;让我知道这是否有帮助。 您是在尝试将变量传递给 Job 还是在 Job 完成后得到响应? 得到回应。它需要是一个包含 utf-8 字符的字符串;作为状态行的一部分,我正在轮询 git 状态更改。我有现有的功能可以做到这一点,我只需要弄清楚如何将其传回即可。我想让它每隔几秒运行一次,每次只更新一个变量,因为在需要 git 信息时运行它并等待它完成会破坏目的(降低绘制延迟)。 明确地说,我实际上并不希望它结束​​。我想生成它并让它在后台轮询并更新父级可访问的变量,通过管道发送等,直到父级关闭。 【参考方案1】:

Boe Prox 在几年前做了一个excellent writeup。他还使用PoshRSJob 模块对此进行了扩展。使用这个模块使这很容易处理。

#Create a synchronized hashtable
$sync = [hashtable]::Synchronized(@
    Time = ''
    Stop = $false
    Updater = ''
)
#create 5 RSJobs
1..5 | Start-RSJob -ScriptBlock 
    param($sync) #accept $sync as a param
    $updater = [Guid]::NewGuid() #unique id per job
    while(-not $sync.Stop)  #run until told not to
        $sync.Time = Get-Date
        $sync.Updater = $updater
        start-sleep -Seconds 1
    
 -ArgumentList $sync #pass $sync as a param

运行此程序会产生 5 个作业:

Id       Name                 State           HasMoreData  HasErrors    Command
--       ----                 -----           -----------  ---------    -------
1        Job1                 Running         False        False        ...
2        Job2                 Running         False        False        ...
3        Job3                 Running         False        False        ...
4        Job4                 Running         False        False        ...
5        Job5                 Running         False        False        ...

然后您可以检查父进程中的 $sync 并查看它是否不断地从这些作业中更新。请注意,这些不仅仅是字符串表示,而是完整的对象。

PS C:\> $sync

Name     Value
----     -----
Time     4/22/2019 11:58:35 AM
Stop     False
Updater  9ab28c51-2941-4866-a064-165b1ceca673

PS C:\> $sync

Name     Value
----     -----
Time     4/22/2019 11:58:37 AM
Stop     False
Updater  113e78a8-1774-4cdf-9638-7235109f0a0d

终止我们设置的作业$sync.Stop = $true

PS C:\> Get-RSJob

Id       Name                 State           HasMoreData  HasErrors    Command
--       ----                 -----           -----------  ---------    -------
1        Job1                 Completed       False        False        ...
2        Job2                 Completed       False        False        ...
3        Job3                 Completed       False        False        ...
4        Job4                 Completed       False        False        ...
5        Job5                 Completed       False        False        ...

【讨论】:

这正是我想要的;非常感谢。 我有什么办法可以做到这一点,以便允许在作业中使用已经导入的函数(导入到主脚本中)? Start-RSJob 具有专门用于此的 -FunctionsToImport 和 -VariablesToImport 参数

以上是关于用于提高状态行性能的 Powershell 作业的主要内容,如果未能解决你的问题,请参考以下文章

提高数据库可用性和性能的作业

用于检查 URL 状态的 PowerShell 脚本

Azure Runbook检查状态

在 PowerShell 中创建批处理作业

PSP(Rocket Software 产品)如何提高 Mainframe 作业的性能

使用 SUM 时提高 MySQL 查询性能