События объекта на основе действий иногда теряются - PullRequest
3 голосов
/ 17 апреля 2020

Предположим, что объект, на который есть ссылка в некотором коде PS, вызвал 100 событий между Register-ObjectEvent и Unregister-Event. Ожидается, что код хоста получит 100 событий. Тем не менее, иногда он получает менее 100, а зачастую намного меньше.

Я вижу, что это последовательно происходит в Windows 7 с PS v3, v4 и v5.1. Простой тестовый пример, приведенный ниже, похоже, работает под Windows 10, но сбои в Windows 7 заставляют меня задуматься, не может ли быть что-то принципиально неправильное со всем подходом.

[EDIT] То же самое происходит в Windows 10 с PS v5.1, только реже, чем в Windows 7. Потребовалось несколько тысяч прогонов с $fired = 10000, чтобы это произошло, но это в конце концов сделал. [конец РЕДАКТИРОВАНИЯ]

Минимальный автономный код для его осуществления (бесстыдно адаптированный из ответа здесь ):

$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 и запущенное в al oop, дает что-то вроде этого (на 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

Любые insgsts очень ценятся.

1 Ответ

0 голосов
/ 27 апреля 2020

Вы «теряете» события, потому что заканчиваете свой сценарий слишком рано. Ваше действие по событию, определенное Register-ObjectEvent ([Utils.StaticEventTest]) Fired -SourceIdentifier $srcId -Action { $global:recvd++ }, будет выполняться асинхронно с вашим сценарием. Если я повторю то, что вы сделали, я получу еще худшие результаты (возможно, из-за виртуальной среды):

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

Но когда я добавлю Start-Sleep -Seconds 5 после вашего for -l oop, я получить этот результат (по крайней мере, в моей среде):

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

Таким образом, все события, которые вы запустили, есть, ни одно событие не будет потеряно. Но если вы хотите увидеть действие, выполненное над ними сразу после их запуска, вам придется подождать, пока все (асинхронные) действия не будут выполнены.

Конечно, Start-Sleep с фиксированным интервалом времени не вариант здесь, потому что вы не можете знать, сколько времени требуется системе для выполнения всех действий. Но это показывает причину, по которой вы думаете, что теряете события.

Возможный способ ожидания ваших событий - это строка после вашего for -l oop:

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

Это работает даже для 100 000 событий на итерацию (действительно пытались):

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

Ни одно событие не потеряно. Это заставляет меня поверить, что стрельба и прослушивание событий работает как положено. Я не могу воспроизвести какие-либо контрмеры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...