基于动作的对象事件有时会丢失 [英] Action-based object events sometimes lost

查看:46
本文介绍了基于动作的对象事件有时会丢失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设已知某些PS代码中引用的对象在 Register-ObjectEvent Unregister-Event 之间触发了100个事件.预期宿主代码将接收100个事件.但是,有时收到的数量少于100,通常少得多.

Suppose an object referenced in some PS code is known to have fired 100 events between Register-ObjectEvent and Unregister-Event. Expectation would be for the host code to receive 100 events. However, it sometimes receives fewer than 100, often times a lot less.

我看到这在带有PS v3,v4和v5.1的Windows 7中持续发生.下面发布的简单测试用例似乎可以在Windows 10下使用,但是Windows 7的失败让我怀疑整个方法是否根本上有问题.

I see this consistently happening in Windows 7 with PS v3, v4 and v5.1. The simple test case posted below appears to work under Windows 10, yet the failures in Windows 7 make me wonder if there is maybe something fundamentally wrong with the whole approach.

 在带有PS v5.1的Windows 10中也是如此,只比Windows 7少./code>来实现,但最终成功了. [结束编辑]

[ EDIT ]  The same happens in Windows 10 with PS v5.1, only less often than in Windows 7. It took a few thousand runs with $fired = 10000 for it to happen, but it eventually did. [ end EDIT ]

实现此目标所需的最少的独立代码(从此处的答案中无耻地改编)

Minimal self-contained code to make it happen (shamelessly adapted from an answer here):

$src = @'
using System;

namespace Utils {
public static class StaticEventTest 
{
    public static event EventHandler Fired;

    public static void RaiseFired()
    {
        if (Fired != null) 
        { 
            Fired(typeof(StaticEventTest), EventArgs.Empty); 
        }
    }
}}
'@

Add-Type -TypeDefinition $src

$fired = 1000
$global:recvd = 0

$srcId = 'Fired'

$id = Register-ObjectEvent ([Utils.StaticEventTest]) Fired `
        -SourceIdentifier $srcId -Action { $global:recvd++ }

for ($i = 1; $i -le $fired; $i++) {
    [Utils.StaticEventTest]::RaiseFired()
}

Unregister-Event -SourceIdentifier $srcId
Receive-Job -Job $id -Wait -AutoRemoveJob

if ($fired -eq $global:recvd) {
    ("total {0} events" -f $fired)
} else {
    ("total {0} events fired - {1} received" -f $fired, $global:recvd)
}

将以上内容另存为 test.ps1 并循环运行会产生如下所示(在Windows 7上):

Having the above saved as test.ps1 and run in a loop gives something like this (on Windows 7):

C:\etc>for /l %n in (1, 1, 10) do @powershell.exe  -executionPolicy remoteSigned -file test.ps1
total 1000 events
total 1000 events
total 1000 events
total 1000 events fired - 391 received
total 1000 events
total 1000 events
total 1000 events fired - 59 received
total 1000 events
total 1000 events fired - 199 received
total 1000 events

任何感激之情.

推荐答案

您正在丢失"事件,因为您太早结束了脚本.您由 Register-ObjectEvent([Utils.StaticEventTest])触发的事件操作-SourceIdentifier $ srcId -Action {$ global:recvd ++} 将与您的脚本异步执行.如果我重复您的操作,则可能会得到更差的结果(可能是由于虚拟环境所致):

You are "losing" events because you end your script too early. Your event action defined by Register-ObjectEvent ([Utils.StaticEventTest]) Fired -SourceIdentifier $srcId -Action { $global:recvd++ } will be executed asynchronously to your script. If I repeat what you did, I get even worse results (maybe due to a virtual environment):

C:\Users\user\Desktop>for /l %n in (1, 1, 10) do @powershell.exe  -executionPolicy remoteSigned -file test.ps1
total 1000 events fired - 17 received
total 1000 events fired - 100 received
total 1000 events fired - 202 received
total 1000 events fired - 16 received
total 1000 events fired - 3 received
total 1000 events fired - 120 received
total 1000 events fired - 1 received
total 1000 events fired - 162 received
total 1000 events fired - 1 received
total 1000 events fired - 27 received

但是当我在 for 循环之后添加 Start-Sleep -Seconds 5 时,我会得到此结果(至少在我的环境中):

But when I add a Start-Sleep -Seconds 5 after your for-loop, I get this result (at least in my environment):

C:\Users\user\Desktop>for /l %n in (1, 1, 10) do @powershell.exe  -executionPolicy remoteSigned -file test.ps1
total 1000 events
total 1000 events
total 1000 events
total 1000 events
total 1000 events
total 1000 events
total 1000 events
total 1000 events
total 1000 events
total 1000 events

因此,您触发的所有事件都在那里,没有任何事件丢失.但是,如果您想查看在触发它们后立即对它们执行的操作,则必须等到所有(异步)操作执行完毕.

So all events you fired are there, no event is lost. But if you want to see the action taken on them right after you fired them, you have to wait until all (asynchronous) actions have been performed.

当然,这里没有固定时间跨度的 Start-Sleep ,因为您不知道系统执行所有操作需要多长时间.但这说明了原因,为什么您认为自己输了比赛.

Of course, Start-Sleep with a fixed time span is not an option here, because you cannot know how long it takes for a system to execute all actions. But it shows the reason, why you think you are losing events.

等待事件的一种可能方法是在 for 循环之后的这一行:

A possible way to wait for your events would be this line after your for-loop:

while ($global:recvd -lt $fired) {Start-Sleep -Milliseconds 10}

这甚至可以为每次迭代处理100.000个事件(确实尝试过):

This even works for 100.000 events per iteration (really tried):

C:\Users\user\Desktop>for /l %n in (1, 1, 10) do @powershell.exe  -executionPolicy remoteSigned -file test.ps1
total 100000 events
total 100000 events
total 100000 events
total 100000 events
total 100000 events
total 100000 events
total 100000 events
total 100000 events
total 100000 events
total 100000 events

没有一个事件丢失.这使我相信,触发和侦听事件可以按预期进行.我无法复制任何反证.

Not a single event lost. This makes me believe that the firing and listening for events works as expected. I am not able to reproduce any counterproof.

这篇关于基于动作的对象事件有时会丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆