1
votes

Comment ajouter une fonction de rappel à un travail asynchrone dans PowerShell et obtenir des données de retour

J'ai cherché sur Internet et j'ai combiné beaucoup de morceaux de code différents, mais je ne réussis tout simplement pas à créer un rappel pour mon travail asynchrone.

L'idée est que je souhaite exécuter plusieurs tâches et, en utilisant les méthodes de rappel, je pourrai analyser la sortie des différents travaux pour modifier certains états et la sortie dans le script principal.

Actuellement, j'ai ceci, et bien que l'événement soit appelé, je ne peux pas obtenir la sortie réelle. Qu'est-ce que j'oublie ici?

$rsPool = [runspacefactory]::CreateRunspacePool(1,2)
$rsPool.Open();

$WebRequest = {
    param($url)
    return Invoke-WebRequest -Uri ($url)
}

$handler = {
  Write-Host "event called";
  Write-Host $_
}

$jobs = @()

$PSinstance = [powershell]::Create();
$PSinstance.AddScript($WebRequest).AddArgument("https://google.com")
$PSinstance.RunspacePool = $rsPool
$jobs += $PSinstance.BeginInvoke()

Register-ObjectEvent -InputObject $PSinstance -EventName InvocationStateChanged -Action $handler;

#$PSinstance | Get-member -MemberType Event

while($jobs.isCompleted -contains $false) {
    Start-Sleep 1
}

$rsPool.close();


0 commentaires

3 Réponses :


0
votes

Essaye celui-là!

»

$rsPool = [runspacefactory]::CreateRunspacePool(1,2)
$rsPool.Open();

$WebRequest = {
    param($url)
    return Invoke-WebRequest -Uri ($url)
}


$jobs = @()

$PSinstance = [powershell]::Create();
$PSinstance.AddScript($WebRequest).AddArgument("https://google.com")
$PSinstance.RunspacePool = $rsPool
$Jobs += [PSCustomObject]@{ Pipe = $PSinstance; Status = $PSinstance.BeginInvoke() }

$results=@()
while ($Jobs.Status -ne $null)
{
    start-sleep -s 1
    write-host "." -nonewline -fore cyan
    foreach ($completedjob in $Jobs|?{ $_.Status.IsCompleted -eq $true })
    {
        $results+=$completedjob.Pipe.EndInvoke($completedjob.Status)
        $completedjob.Status = $null
    }
}

$rsPool.close();

$results|out-host

»


1 commentaires

Cela n'utilise malheureusement pas la fonction de rappel et ne fonctionnerait pas pour moi car j'ai besoin d'analyser les données de sortie à la volée à partir de différents emplois



0
votes

J'ai fini par changer la méthode Invoke et l'enregistreur d'événements pour passer un paramètre contenant la sortie.

Bien que ce ne soit probablement pas la méthode la plus propre, cela fonctionne pour moi (avec quelques vérifications en place pour que les données ne soient pas accessibles avant qu'elles ne soient réellement disponibles, bien sûr.

$Object = New-Object 'System.Management.Automation.PSDataCollection[psobject]';

$jobs += $PSinstance.BeginInvoke($Object, $Object);

Add-Member -InputObject $PSinstance -MemberType NoteProperty -Name EventSubscriber -Value (
    Register-ObjectEvent -InputObject $PSinstance -EventName InvocationStateChanged -MessageData $Object -Action {
        # Ignore initial state change on startup
        if ($event.InvocationStateInfo.State -eq [System.Management.Automation.PSInvocationState]::Running) {
            return;
        }

        Write-Host "event called";
        Write-Host $Object.StatusCode;
    }
);

# Can be accessed after the invoke has finished.
Write-Host "The result is: $Object.StatusCode";


0 commentaires

0
votes

Au lieu d'utiliser le SDK PowerShell assez complexe , s'il s'agit simplement d'exécuter les requêtes Web en parallèle et qu'il est acceptable d'attendre de manière synchrone qu'elles se terminent,
ForEach-Object -Parallel - disponible dans PowerShell v7 + - offre une solution simple:

$jobs = 'https://google.com', 'http://example.org' | ForEach-Object {
  $uri = $_
  Start-ThreadJob { Invoke-WebRequest -Uri $using:uri }
}

# ... do other things

# Now wait synchronously (though you could poll the state in a loop instead)
# and output the responses.
$jobs | Receive-Job -Wait -AutoRemoveJob

Au lieu d'attendre de manière synchrone, vous pouvez également utiliser le commutateur -AsJob pour -AsJob les objets de travail [1] , de manière asynchrone, que vous pourrez gérer ultérieurement avec les applets de commande *-Job ordinaires ( Wait-Job , Receive-Job , ...):

# Uses threads to run two Invoke-WebRequest calls in parallel.
# Asynchronously returns job objects that can be monitored later.
$jobs = 'https://google.com', 'http://example.org' | ForEach-Object -AsJob -Parallel {
  Invoke-WebRequest -Uri $_
}

# ... do other things

# Now wait synchronously (though you could poll the state instead).
# and output the responses.
$jobs | Receive-Job -Wait -AutoRemoveJob

Dans les versions antérieures de PowerShell, vous pouvez utiliser la cmdlet Start-ThreadJob (fournie avec la version 6 +, installable via Install-Module ThreadJob dans les versions antérieures) [2] :

# Uses threads to run two Invoke-WebRequest calls in parallel,
# synchronously waits for both to finish and collects the responses.
$responses = 'https://google.com', 'http://example.org' | ForEach-Object -Parallel {
  Invoke-WebRequest -Uri $_
}

Remarque: Bien que vous puissiez également utiliser des travaux d'arrière - plan classiques basés sur des processus enfants , créés avec Start-Job , les options basées sur les threads ci-dessus fonctionnent beaucoup mieux - voir about_Jobs .


[1] Instances de [System.Management.Automation.PSTasks.PSTaskJob] , un type non documenté actuellement qui dérive de System.Management.Automation.Job , qui garantit que les instances peuvent être utilisées avec les applets de commande *-Job .

[2] Start-ThreadJob renvoie des instances de [ThreadJob.ThreadJob] , un type non documenté actuellement qui dérive de System.Management.Automation.Job2 , qui à son tour dérive de System.Management.Automation.Job


0 commentaires