- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
从同一个线程触发的事件应该按照它们被触发的顺序接收,这听起来像是一个合理的期望。然而,情况似乎并非如此。这是已知/记录在案的行为吗?是否有任何方法可以纠正它?
下面是两个可立即运行的代码片段,显示了该问题,在 Win7 和 Win10 下使用 PS v5.1 进行了测试。
(a) 从单独作业(即不同进程)中的线程触发的事件。
$events = 1000
$recvd = 0
$ooseq = 0
$job = Register-EngineEvent -SourceIdentifier 'Posted' -Action {
$global:recvd++
if($global:recvd -ne $event.messageData) {
$global:ooseq++
("-?- event #{0} received as #{1}" -f $event.messageData, $global:recvd)
} }
$run = Start-Job -ScriptBlock {
Register-EngineEvent -SourceIdentifier 'Posted' -Forward
for($n = 1; $n -le $using:events; $n++) {
[void] (New-Event -SourceIdentifier 'Posted' -MessageData $n)
} }
Receive-Job -Job $run -Wait -AutoRemoveJob
Unregister-Event -SourceIdentifier 'Posted'
Receive-Job -Job $job -Wait -AutoRemoveJob
if($events -eq $script:recvd) {
("total {0} events" -f $events)
} else {
("total {0} events events, {1} received" -f $events, $recvd)
}
if($ooseq -ne 0) {
("{0} out-of-sequence events" -f $ooseq)
}
失败案例的示例输出(一批 100 次连续运行)。
-?- event #545 received as #543
-?- event #543 received as #544
-?- event #546 received as #545
-?- event #544 received as #546
total 1000 events
4 out-of-sequence events
(b) 从单独的运行空间(即不同的线程)触发的事件。
$recvd = 0
$ooseq = 0
$job = Register-EngineEvent -SourceIdentifier 'Posted' -Action {
$global:recvd++
if($recvd -ne $event.messageData) {
$global:ooseq++
("-?- event #{0} received as #{1}" -f $event.messageData, $recvd)
}}
$sync = [hashTable]::Synchronized(@{})
$sync.Host = $host
$sync.events = 1000
$sync.posted = 0
$rs = [runspaceFactory]::CreateRunspace()
$rs.ApartmentState = "STA"
$rs.ThreadOptions = "ReUseThread"
$rs.Open()
$rs.SessionStateProxy.SetVariable("sync",$sync)
$ps = [powerShell]::Create().AddScript({
for($n = 1; $n -le $sync.events; $n++) {
$sync.Host.Runspace.Events.GenerateEvent('Posted', $null, $null, $n)
$sync.posted++
}})
$ps.runspace = $rs
$thd = $ps.BeginInvoke()
$ret = $ps.EndInvoke($thd)
$ps.Dispose()
Unregister-Event -SourceIdentifier 'Posted'
Receive-Job -Job $job -Wait -AutoRemoveJob
if($sync.events -eq $recvd) {
("total {0} events" -f $sync.events)
} else {
("total {0} events fired, {1} posted, {2} received" -f $sync.events, $sync.posted, $recvd)
}
if($ooseq -ne 0) {
("{0} out-of-sequence events" -f $ooseq)
}
失败案例类似于上面 (a) 下发布的示例,除了少数运行也完全丢弃了事件。然而,这更可能与另一个问题 Action-based object events sometimes lost 有关。 .
total 1000 events fired, 1000 posted, 999 received
484 out-of-sequence events
接收操作(其中 $global:recvd++
)总是在同一个托管线程上调用(这通过保存和比较 [System.Threading.Thread] 得到确认)::CurrentThread.ManagedThreadId
调用之间);
在执行期间不会重新输入接收操作(通过添加全局“嵌套”计数器确认,将操作包装在 [System.Threading.Interlocked] 之间)::Increment/Decrement
调用并检查计数器从不取 0
和 1
以外的任何值。
这些消除了几个可能的竞争条件,但仍然没有解释观察到的行为发生的原因或如何纠正它,所以最初的问题仍然悬而未决。
最佳答案
Is this a known/documented behavior?
“通常”事件处理是异步的设计。在 PowerShell 中,使用 Register-EngineEvent -Action
之类的 cmdlet 就是这种情况。这确实是已知和预期的行为。您可以阅读有关 PowerShell 事件的更多信息 here和 here .两个微软消息来源都指出,这种事件处理方式是异步的:
PowerShell Eventing lets you respond to the asynchronous notifications that many objects support.
和
NOTE These cmdlets can only be used for asynchronous .NET events. It’s not possible to set up event handlers for synchronous events using the PowerShell eventing cmdlets. This is because synchronous events all execute on the same thread and the cmdlets expect (require) that the events will happen on another thread. Without the second thread, the PowerShell engine will simply block the main thread and nothing will ever get executed.
这基本上就是您正在做的事情。您将后台作业中的事件转发给定义了操作的事件订阅者,并在不阻塞后台作业的情况下执行操作。据我所知,没有什么可以期待的了。没有指定以任何特殊顺序处理转发事件的要求。即使是 -Forward
开关也不能确保任何更多,除了传递事件:
Indicates that the cmdlet sends events for this subscription to the session on the local computer. Use this parameter when you are registering for events on a remote computer or in a remote session.
很难甚至不可能找到有关 cmdlet 内部结构的任何文档。请记住,Microsoft 不会发布任何关于过去内部 afaik 的文档,而是由 MVP 猜测内部发生的事情并撰写有关它的书籍(激烈表达)。
因此,由于不需要按特定顺序处理事件,而 PowerShell 的任务只是对事件队列执行操作,因此也允许并行执行这些操作以加速事件队列的处理.
在只有一个 vCPU 的虚拟机上测试您的脚本。错误的顺序有时仍会发生,但很少见。因此(真正的)并行性越少,打乱顺序的可能性就越小。当然,您不能阻止由在一个物理内核上执行的不同线程实现的逻辑并行性。所以仍然存在一些“错误”。
Is there any recourse to correct it?
我把“正常”放在引号里,因为有同步实现的方法。您必须实现自己的 System.EventHandler
类型的事件处理程序。我推荐阅读 this article获取实现示例。
另一种解决方法是将事件存储在自己的事件队列中,并在收集后对它们进行排序(在 ISE 中运行,尚未在 PS 中运行):
$events = 10000
$recvd = 0
$ooseq = 0
$myEventQueue = @()
$job = Register-EngineEvent -SourceIdentifier 'Posted' -Action {$global:myEventQueue += $event}
$run = Start-Job -ScriptBlock {
Register-EngineEvent -SourceIdentifier 'Posted' -Forward
for($n = 1; $n -le $using:events; $n++) {
[void] (New-Event -SourceIdentifier 'Posted' -MessageData $n)
}
}
Receive-Job -Job $run -Wait -AutoRemoveJob
Unregister-Event -SourceIdentifier 'Posted'
Receive-Job -Job $job -Wait -AutoRemoveJob
Write-Host "Wrong event order in unsorted queue:"
$i = 1
foreach ($event in $myEventQueue) {
if ($i -ne $event.messageData) {
Write-Host "Event $($event.messageData) received as event $i"
}
$i++
}
$myEventQueue = $myEventQueue | Sort-Object -Property EventIdentifier
Write-Host "Wrong event order in sorted queue:"
$i = 1
foreach ($event in $myEventQueue) {
if ($i -ne $event.messageData) {
Write-Host "Event $($event.messageData) received as event $i"
}
$i++
}
存档链接:
关于乱序接收的 PowerShell 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61313233/
我是一名优秀的程序员,十分优秀!