用于提高状态行性能的 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 作业的主要内容,如果未能解决你的问题,请参考以下文章