我如何在Powershell类构造函数中使用异步或并行?



我有一个Powershell类(一对,实际上;它是嵌套的)我在循环中创建实例。构造函数有一堆填充对象的任务(包括那些嵌套类,它们也填充自己)。然而,这些任务有时有点慢,我希望能够同时执行多个任务并实例化多个对象。我如何在Powershell中做到这一点?

示例类:

Class Server {
Server([string] $ServerName) {
$this.ServerName = $ServerName
$this.ComputerSystem = Get-CimInstance Win32_ComputerSystem -ComputerName $ServerName 
$this.OperatingSystem = Get-CimInstance Win32_OperatingSystem -ComputerName $ServerName
$this.Bios = Get-CimInstance -ClassName Win32_BIOS -ComputerName $ServerName
$this.NetworkAdapter = Get-CimInstance Win32_NetworkAdapterConfiguration  -ComputerName $ServerName
}
}

不幸的是,没有方便的解决方案从PowerShell 7.3.0开始,因为PowerShell自定义class中的属性不能通过访问器方法实现。.

  • 在GitHub issue #2219中提出了在未来版本中添加此功能-多年前。

:

  • 使用方法(如.Bios())代替属性(如.Bios),因此"属性值"可动态检索;用hidden属性隐藏的后台实例属性是一个映射"属性名称"的哈希表。

  • 使用Start-ThreadJob(PowerShell (Core) 7+一起发货),并且可以与Install-Module ThreadJob一起安装在Windows PowerShell中)来异步启动基于线程的施工作业,这些作业在后台执行Get-CimInstance调用。

Class Server {
# Instance variables:
[string] $ServerName
# A hidden map (hashtable) that maps what are conceptually properties to the
# commands that retrieve their values, via thread jobs.
# Note that the static values defined here are *script blocks*, which are replaced
# with what their *invocation* evaluates to in the constructor.
# Note: By default, up to 5 thread jobs are permitted to run at a time.
#       You can modify this limit with -ThrottleLimit on the first Start-ThreadJob call,
#       which then applies to all subsequent Start-ThreadJob calls that do not themselves
#       use -ThrottleLimit in the same session.
hidden [hashtable] $_jobsMap = @{
ComputerSystem = { Start-ThreadJob { Get-CimInstance Win32_ComputerSystem -ComputerName $using:ServerName } }
OperatingSystem = { Start-ThreadJob { Get-CimInstance Win32_OperatingSystem -ComputerName $using:ServerName } }
Bios = { Start-ThreadJob { Get-CimInstance -ClassName Win32_BIOS -ComputerName $using:ServerName } }
NetworkAdapter = { Start-ThreadJob { Get-CimInstance Win32_NetworkAdapterConfiguration -ComputerName $using:ServerName } }
}

# Constructor
Server([string] $ServerName) {
$this.ServerName = $ServerName
# Asynchronously start the thread jobs that populate the "property"
# values, i.e. the entries of the $this._jobsMap hashtable.
foreach ($key in @($this._jobsMap.Keys)) {
# Replace each predefined script block with the result of its
# *invocation*, i.e. with an object describing each launched thread job.
$this._jobsMap[$key] = & $this._jobsMap[$key]
}
}

# Methods that act like property accessors.
[object] ComputerSystem() {
return $this.get('ComputerSystem')
}
[object] OperatingSystem() {
return $this.get('OperatingSystem')
}
[object] Bios() {
return $this.get('Bios')
}
[object] NetworkAdapter() {
return $this.get('NetworkAdapter')
}

# Hidden helper method that returns the value of interest,
# making sure that a value has been received from the relevant
# thread job first.
hidden [object] get($propName) {
if ($this._jobsMap[$propName] -is [System.Management.Automation.Job]) {
# Note that any error-stream output from the jobs
# is *not* automatically passed through; -ErrorVariable is used
# to collect any error(s), which are translated into a script-terminating
# error with `throw`
$e = $null
$this._jobsMap[$propName] = $this._jobsMap[$propName] | 
Receive-Job -Wait -AutoRemoveJob -ErrorVariable e
if ($e) {
throw $e[0]
}
}
return $this._jobsMap[$propName]
}

# Method that indicates whether *all* "properties" have finished
# initializing.
[bool] IsInitialized() {
$pendingJobs = $this._jobsMap.Values.Where({ $_ -is [System.Management.Automation.Job] })
return $pendingJobs.Count -eq 0 -or $pendingJobs.Where({ $_.State -in 'NotStarted', 'Running' }, 'First').Count -eq 0
}

} 

使用例子:

# Instantiate [Server], which asynchronously starts the thread jobs
# that will populate the properties.
$server = [Server]::new('.')
# Access a "property" by way of its helper method, which
# waits for the job to complete first, if necessary.
$server.Bios() 
# Test if all property values have been retrieved yet.
$server.IsInitialized()

最新更新