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();
3 Réponses :
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
»
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
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";
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