about_Thread_Jobs

Descrizione breve

Fornisce informazioni sui processi basati su thread di PowerShell. Un processo thread è un tipo di processo in background che esegue un comando o un'espressione in un thread separato all'interno del processo di sessione corrente.

Descrizione lunga

PowerShell esegue contemporaneamente comandi e script tramite processi. Esistono tre tipi di processi forniti da PowerShell per supportare la concorrenza.

  • RemoteJob - Comandi e script eseguiti in una sessione remota. Per informazioni, vedere about_Remote_Jobs.
  • BackgroundJob - Comandi e script eseguiti in un processo separato nel computer locale. Per altre informazioni, vedere About Jobs (Informazioni sui processi).
  • PSTaskJob oppure ThreadJob - I comandi e gli script vengono eseguiti in un thread separato all'interno dello stesso processo nel computer locale.

I processi basati su thread non sono affidabili come i processi remoti e in background, perché vengono eseguiti nello stesso processo in thread diversi. Se un processo presenta un errore critico che arresta in modo anomalo il processo, tutti gli altri processi del processo vengono terminati.

Tuttavia, i processi basati su thread richiedono un sovraccarico inferiore. Non usano il livello remoto o la serializzazione. Gli oggetti risultato vengono restituiti come riferimenti agli oggetti attivi nella sessione corrente. Senza questo sovraccarico, i processi basati su thread vengono eseguiti più velocemente e usano meno risorse rispetto agli altri tipi di processo.

Importante

La sessione padre che ha creato il processo monitora anche lo stato del processo e raccoglie i dati della pipeline. Il processo figlio del processo viene terminato dal processo padre dopo che il processo raggiunge uno stato completato. Se la sessione padre viene terminata, tutti i processi figlio in esecuzione vengono terminati insieme ai processi figlio.

Esistono due modi per ovviare a questa situazione:

  1. Usare Invoke-Command per creare processi eseguiti in sessioni disconnesse. Per altre informazioni, vedere about_Remote_Jobs.
  2. Usare Start-Process per creare un nuovo processo anziché un processo. Per altre informazioni, vedere Start-Process.

Come avviare e gestire processi basati su thread

Esistono due modi per avviare processi basati su thread:

  • Start-ThreadJob- dal modulo ThreadJob
  • ForEach-Object -Parallel -AsJob - La funzionalità parallela è stata aggiunta in PowerShell 7.0

Usare gli stessi cmdlet di processo descritti in about_Jobs per gestire i processi basati su thread.

uso di Start-ThreadJob

Il modulo ThreadJob fornito per la prima volta con PowerShell 6. Può anche essere installato da PowerShell Gallery per Windows PowerShell 5.1.

Per avviare un processo di thread nel computer locale, usare il Start-ThreadJob cmdlet con un comando o uno script racchiuso tra parentesi graffe ({ }).

Nell'esempio seguente viene avviato un processo di thread che esegue un Get-Process comando nel computer locale.

Start-ThreadJob -ScriptBlock { Get-Process }

Il Start-ThreadJob comando restituisce un ThreadJob oggetto che rappresenta il processo in esecuzione. L'oggetto processo contiene informazioni utili sul processo, incluso il relativo stato di esecuzione corrente. Raccoglie i risultati del processo durante la generazione dei risultati.

uso di ForEach-Object -Parallel -AsJob

PowerShell 7.0 ha aggiunto un nuovo set di parametri al ForEach-Object cmdlet . I nuovi parametri consentono di eseguire blocchi di script in thread paralleli come processi di PowerShell.

È possibile inviare i dati tramite pipe a ForEach-Object -Parallel. I dati vengono passati al blocco di script eseguito in parallelo. Il -AsJob parametro crea oggetti processi per ognuno dei thread paralleli.

Il comando seguente avvia un processo che contiene processi figlio per ogni valore di input inviato tramite pipe al comando. Ogni processo figlio esegue il Write-Output comando con un valore di input inviato tramite pipe come argomento.

1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob

Il ForEach-Object -Parallel comando restituisce un PSTaskJob oggetto che contiene processi figlio per ogni valore di input inviato tramite pipe. L'oggetto processo contiene informazioni utili sullo stato di esecuzione dei processi figlio. Raccoglie i risultati dei processi figlio durante la generazione dei risultati.

Come attendere il completamento di un processo e recuperare i risultati del processo

È possibile usare i cmdlet di processo di PowerShell, ad esempio Wait-Job e Receive-Job per attendere il completamento di un processo e quindi restituire tutti i risultati generati dal processo.

Il comando seguente avvia un processo thread che esegue un Get-Process comando, quindi attende il completamento del comando e infine restituisce tutti i risultati dei dati generati dal comando.

Start-ThreadJob -ScriptBlock { Get-Process } | Wait-Job | Receive-Job

Il comando seguente avvia un processo che esegue un Write-Output comando per ogni input inviato tramite pipe, quindi attende il completamento di tutti i processi figlio e infine restituisce tutti i risultati dei dati generati dai processi figlio.

1..5 | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job

Il Receive-Job cmdlet restituisce i risultati dei processi figlio.

1
3
2
4
5

Poiché ogni processo figlio viene eseguito in parallelo, l'ordine dei risultati generati non è garantito.

Prestazioni del processo thread

I processi thread sono più veloci e più leggeri rispetto ad altri tipi di processi. Ma hanno ancora un sovraccarico che può essere elevato rispetto al lavoro che il lavoro sta facendo.

PowerShell esegue comandi e script in una sessione. Un solo comando o script può essere eseguito alla volta in una sessione. Pertanto, quando si eseguono più processi, ogni processo viene eseguito in una sessione separata. Ogni sessione contribuisce al sovraccarico.

I processi di thread offrono prestazioni ottimali quando il lavoro eseguito è maggiore del sovraccarico della sessione usata per eseguire il processo. Esistono due casi per soddisfare questi criteri.

  • Il lavoro è a elevato utilizzo di calcolo: l'esecuzione di uno script in più processi di thread può sfruttare più core del processore e completare più velocemente.

  • Il lavoro è costituito da un'attesa significativa: uno script che impiega tempo in attesa dei risultati delle chiamate remote o di I/O. L'esecuzione in parallelo in genere viene completata più rapidamente rispetto all'esecuzione sequenziale.

(Measure-Command {
    1..1000 | ForEach { Start-ThreadJob { Write-Output "Hello $using:_" } } | Receive-Job -Wait
}).TotalMilliseconds
36860.8226

(Measure-Command {
    1..1000 | ForEach-Object { "Hello: $_" }
}).TotalMilliseconds
7.1975

Il primo esempio precedente mostra un ciclo foreach che crea 1000 processi thread per eseguire una scrittura di stringa semplice. A causa del sovraccarico del processo, il completamento richiede più di 36 secondi.

Il secondo esempio esegue il ForEach cmdlet per eseguire le stesse 1000 operazioni. Questa volta, ForEach-Object viene eseguito in sequenza, in un singolo thread, senza alcun sovraccarico del processo. Viene completato in soli 7 millisecondi.

Nell'esempio seguente vengono raccolte fino a 5000 voci per 10 log di sistema separati. Poiché lo script comporta la lettura di una serie di log, è opportuno eseguire le operazioni in parallelo.

$logNames.count
10

Measure-Command {
    $logs = $logNames | ForEach-Object {
        Get-WinEvent -LogName $_ -MaxEvents 5000 2>$null
    }
}

TotalMilliseconds : 252398.4321 (4 minutes 12 seconds)
$logs.Count
50000

Lo script viene completato nella metà del tempo in cui i processi vengono eseguiti in parallelo.

Measure-Command {
    $logs = $logNames | ForEach {
        Start-ThreadJob {
            Get-WinEvent -LogName $using:_ -MaxEvents 5000 2>$null
        } -ThrottleLimit 10
    } | Wait-Job | Receive-Job
}

TotalMilliseconds : 115994.3 (1 minute 56 seconds)
$logs.Count
50000

Processi e variabili di thread

Esistono diversi modi per passare valori nei processi basati su thread.

Start-ThreadJob può accettare variabili inoltrate tramite pipe al cmdlet, passate al blocco di script tramite la $using parola chiave o passate tramite il parametro ArgumentList .

$msg = "Hello"

$msg | Start-ThreadJob { $input | Write-Output } | Wait-Job | Receive-Job

Start-ThreadJob { Write-Output $using:msg } | Wait-Job | Receive-Job

Start-ThreadJob { param ([string] $message) Write-Output $message } -ArgumentList @($msg) |
  Wait-Job | Receive-Job

ForEach-Object -Parallel accetta pipe nelle variabili e le variabili passate direttamente al blocco di script tramite la $using parola chiave .

$msg = "Hello"

$msg | ForEach-Object -Parallel { Write-Output $_ } -AsJob | Wait-Job | Receive-Job

1..1 | ForEach-Object -Parallel { Write-Output $using:msg } -AsJob | Wait-Job | Receive-Job

Poiché i processi thread vengono eseguiti nello stesso processo, qualsiasi tipo di riferimento variabile passato al processo deve essere trattato con attenzione. Se non è un oggetto thread-safe, non deve mai essere assegnato e le proprietà e i metodi non devono mai essere richiamati su di esso.

Nell'esempio seguente viene passato un oggetto .NET ConcurrentDictionary thread-safe a tutti i processi figlio per raccogliere oggetti di processo denominati in modo univoco. Poiché si tratta di un oggetto thread-safe, può essere usato in modo sicuro mentre i processi vengono eseguiti simultaneamente nel processo.

$threadSafeDictionary = [System.Collections.Concurrent.ConcurrentDictionary[string,object]]::new()
$jobs = Get-Process | ForEach {
    Start-ThreadJob {
        $proc = $using:_
        $dict = $using:threadSafeDictionary
        $dict.TryAdd($proc.ProcessName, $proc)
    }
}
$jobs | Wait-Job | Receive-Job

$threadSafeDictionary.Count
96

$threadSafeDictionary["pwsh"]

NPM(K)  PM(M)   WS(M) CPU(s)    Id SI ProcessName
------  -----   ----- ------    -- -- -----------
  112  108.25  124.43  69.75 16272  1 pwsh

Vedi anche