在事件驱动脚本中,我正在寻找一种从后台线程作业或运行空间为主线程触发自定义事件的方法。 后台处理非常耗时并且不能异步,这就是它被委托给后台线程作业或运行空间的原因。 脚本的主要部分是事件驱动的,以避免定期轮询和不必要的资源消耗。
脚本的总体结构如下。
$DataCollectionScriptBlock = {
#
... Initialization stuff
#
while ( $stopRequested -ne 'yes' ) {
#
... collect / filter / consolidate data
#
fire event 'datacollection' for main Thread with necessary data as event argument
#
}
#
... termination stuff
#
}
$DataConsumerScriptBlock = {
# retrieve necessary data
$data = $Event.SourceEventArgs
#
... perform necessary stuff
#
}
#
# main thread operations
#
...
# start background job (or runspace)
Start-ThreadJob -ScriptBlock $DataCollectionScriptBlock -InputObject <something> ...
# subscibe to datacollection event
Register-EngineEvent -SourceIdentifier 'datacollection' -Action $DataConsumerScriptBlock
#
...
#
Wait-Event
为了在 $DataCollectionScriptBlock 中生成事件,我尝试了 New-Event cmdlet,但它在 threadjob/runspace 线程中本地生成事件。 该事件不能在主线程中使用。
我也尝试过使用BackgroundWorker类但没有成功。
我还阅读了该论坛上的几篇帖子,其中有大量 C# 示例,但没有找到在 Powershell 中实现的简单解决方案。
Start-Job
通过隐藏子进程使用跨进程并行性;因此,不支持使用events(这是一个in-process功能)。
但是,通过基于线程的并行性,其中多个运行空间并行运行在同一进程中,解决方案是可能的,基于:
识别应接收事件的目标运行空间,在独立 PowerShell 会话中始终是 Get-Runspace
(
(Get-Runspace)[0]
) 报告的first运行空间
GenerateEvent()
实例的 System.Management.Automation.Runspaces.Runspace
属性上调用 .Events
方法。
为简单起见,以下独立示例使用PowerShell(核心)7+的
-Parallel
cmdlet的
ForEach-Object
功能,该功能可创建并行运行空间并同步执行它们。 Start-ThreadJob
(随 PowerShell v7+ 一起提供;可在 Windows PowerShell 中按需安装)以及通过 PowerShell SDK 手动创建的运行空间工作。
#
# main thread operations
#
# Subscribe to a custom datacollection event
$job = Register-EngineEvent -SourceIdentifier datacollection -Action {
# Sample event processing that prints directly to the display.
@"
Event received:
Sender: $($Event.Sender)
Event args: $($Event.SourceArgs | Out-String)
"@ | Out-Host
}
# Use ForEach-Object -Parallel to create two parallel runspaces,
# and make them each trigger an event for the main runspace.
# I expect this to work equally with Start-ThreadJob and manual runspace creation.
1..2 | ForEach-Object -Parallel {
$null =
(Get-Runspace)[0].Events.GenerateEvent(
'datacollection', # event name
$_, # sender
@{ foo = 'bar' + $_ }, # event arguments
$null # extra data
)
}
# Keep the script alive until Ctrl-C is pressed
# (Due to use of an -Action script block with Register-EngineEvent,
# Wait-Process doesn't output any of the generated events.
# It just waits indefinitely, during which time the -Action script block can run).
try {
Wait-Event
} finally {
# Clean up.
$job | Remove-Job -Force
}
您应该看到以下显示输出,显示两个事件均在主运行空间中处理(不能保证事件的顺序,并且可能会有所不同):
Event received:
Sender: 2
Event args:
Name Value
---- -----
foo bar2
Event received:
Sender: 1
Event args:
Name Value
---- -----
foo bar1