In an event-driven script, I'm looking for a way to fire a custom event for the main thread from a background threadjob or runspace.
The background treatment is time consuming and cannot be async, this is why it is delegated to a background threadjob or runspace.
The main part of the script is event-driven to avoid periodic polling and unnecessary resource consumption.
在事件驱动的脚本中,我正在寻找一种从后台线程作业或运行空间为主线程触发自定义事件的方法。后台处理是耗时的,并且不能被重写,这就是为什么它被委托给后台线程作业或运行空间的原因。脚本的主要部分是事件驱动的,以避免定期轮询和不必要的资源消耗。
The general structure of the script is as follows.
脚本的一般结构如下。
$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
To generate the event in $DataCollectionScriptBlock I tried the New-Event cmdlet, but it generates the event locally in the threadjob/runspace thread.
The event cannot be used in the main thread.
为了在$DataCollectionScriptBlock中生成事件,我尝试了New-Event cmdlet,但它在线程作业/运行空间线程中本地生成事件。该事件不能在主线程中使用。
I've also tried to use the BackgroundWorker class without success.
我也尝试过使用Backp.Worker类,但没有成功。
I've also read several posts on this forum with numerous examples in C# but without finding a simple solution to implement in Powershell.
我还读了这个论坛上的几篇帖子,其中有许多用C#编写的示例,但没有找到在PowerShell中实现的简单解决方案。
更多回答
"The event cannot be used in the main thread" ? What is that supposed to mean? I assume you mean, the callback is not supposed to run in the main thread. Your question is pretty broad but in the end, C# is a much better language to handle this than PowerShell will ever be.
“该事件不能在主线程中使用”?这是什么意思?我想您的意思是,回调不应该在主线程中运行。你的问题相当广泛,但归根结底,C#是一种比PowerShell更好的语言来处理这个问题。
I mean : the event generated by New-Event in the background job/runspace remains local and is not queued in th event queue of the main thread. For sure C# is much better but my knowledge of C# is very limited. Moreover I am optimizing existing scripts already written in powershell. Do you know example of class that could be used in scropts (embed C# code) ?
我的意思是:由后台作业/运行空间中的New-Event生成的事件仍然是本地的,不会在主线程的事件队列中排队。当然,C#要好得多,但我对C#的了解非常有限。此外,我正在优化已用PowerShell编写的现有脚本。你知道可以在脚本中使用的类的例子(嵌入C#代码)吗?
Can you give a specific example of what you need to handle with your code? For me at least, your question is too broad and I'm not sure what you're expecting for answer. Something worth noting from your example is that Start-Job
is definitely not the right, if you want event handling in a different thread you need to look into runspaces or their simplified version (ThreadJob).
你能举一个具体的例子来说明你需要用你的代码来处理什么吗?至少对我来说,你的问题太笼统了,我不确定你希望得到什么答案。从您的示例中值得注意的一点是,Start-Job绝对不是正确的,如果您希望在不同的线程中处理事件,您需要查看运行空间或它们的简化版本(线程作业)。
优秀答案推荐
Start-Job
uses cross-process parallelism, via a hidden child process; therefore, using events - which are an in-process feature - isn't supported.
Start-Job通过隐藏子进程使用跨进程并行性;因此,不支持使用作为进程内功能的事件。
However, via thread-based parallelism, where multiple runspaces run in parallel in the same process, a solution is possible, based on:
然而,通过基于线程的并行(其中多个运行空间在同一进程中并行运行),基于以下条件的解决方案是可能的:
Identifying the target runspace that should receive an event, which in stand-alone PowerShell session is always the first runspace reported by Get-Runspace
((Get-Runspace)[0]
标识应接收事件的目标运行空间,该目标运行空间在独立PowerShell会话中始终是Get-Runspace((Get-Runspace)[0]报告的第一个运行空间
Calling the GenerateEvent()
method on the resulting System.Management.Automation.Runspaces.Runspace
instance's .Events
property.
对结果System.Management.Automation.Runspaces.Runspace实例的.Events属性调用GenerateEvent()方法。
For simplicity, the following self-contained example uses the PowerShell (Core) 7+'s -Parallel
feature of the ForEach-Object
cmdlet, which creates parallel runspaces and executes them synchronously.
However, I would expect the code to work equally with Start-ThreadJob
(ships with PowerShell v7+; installable on demand in Windows PowerShell) as well as with manually created runspaces via the PowerShell SDK.
为简单起见,以下自包含的示例使用了ForEach-Object cmdlet的PowerShell(Core)7+‘S并行功能,该功能可创建并行运行空间并同步执行它们。然而,我希望代码与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
}
You should see the following display output, showing that both events were processed in the main runspace (ordering of the events isn't guaranteed and can vary):
您应该看到以下显示输出,显示这两个事件都是在主运行空间中处理的(不保证事件的顺序,可能会有所不同):
Event received:
Sender: 2
Event args:
Name Value
---- -----
foo bar2
Event received:
Sender: 1
Event args:
Name Value
---- -----
foo bar1
更多回答
Many thanks, it is working like a charm also with Start-ThreadJob.
非常感谢,它的工作就像一个魔咒,也开始-线程作业。
Initial post edited to replace Job with ThreadJob.
最初的帖子被编辑为用线程作业取代作业。
Glad to hear it, @Alain; my pleasure.
“很高兴听你这么说,”阿兰;我的荣幸。
我是一名优秀的程序员,十分优秀!