Cara Menjalankan Powershell Pipeline Secara Asinkron

Bagaimana cara membuat Powershell mengeksekusi jaringan pipa secara asinkron, bukan 'berurutan'?

Contoh untuk menggambarkan hal ini:

Function PipeDelay($Name, $Miliseconds){
    Process {
        Sleep -miliseconds $Miliseconds
        Write-Host "$Name : $_"
        $_        
    }
}

1..7 | PipeDelay Fast 100 | PipeDelay Slow 300 | PipeDelay Slowest 900

Output di ISE dan PowerShell adalah sebagai berikut:

Fast : 1
Slow : 1
Slowest : 1
1
Fast : 2
Slow : 2
Slowest : 2
2
Fast : 3
Slow : 3
Slowest : 3
3
...

Seolah-olah Powershell menjalankan seluruh pipa secara berurutan untuk satu objek masukan sebelum memproses objek masukan berikutnya. Eksekusinya terlihat seperti ini:

Fast    :  1                         2                         3
Slow    :    1---1                     2---2                     3---3
Slowest :          1---------------1         2---------------2         3-----...

Apakah mungkin dengan beberapa pengaturan/variabel lingkungan/dll. untuk membuat saluran pipa berjalan secara mandiri/asinkron? mungkin dengan pengaturan tentang ukuran buffer pipa, dll.? Sehingga eksekusinya akan terlihat seperti ini:

Fast    :  1 2 3 4 5 6 7
Slow    :    1---1 2---2 3---3 4---4 5---5 6---6 7---7
Slowest :          1---------------1 2---------------2 3-----...

CATATAN

Saya pikir itu karena mode STA/MTA. Saya tidak memahaminya sepenuhnya tetapi hasil yang sama di ISE (STA)/Powershell Shell (MTA) tampaknya menghilangkan mode STA/MTA sebagai penyebabnya.

Selain itu, saya pikir Write-Host adalah masalah yang memaksa pipa diproses secara berurutan, tetapi meskipun saya mengganti Write-Host dengan New-Event, pemrosesan berurutan tetap berlaku.


person tkokasih    schedule 14.10.2014    source sumber
comment
Apa yang Anda tanyakan tidak akan mudah, salah satu keuntungan dari pipeline adalah tidak ketujuh objek ada di memori pada satu waktu; namun cara Anda menampilkan ketujuhnya akan di-cache dengan cepat dan tidak akan diselesaikan hingga penyelesaian paling lambat. Anda perlu membuat throttle ke dalam pipeline sehingga jika Anda bekerja dengan objek 5GB+, memori Anda tidak akan melebihi. load-from-file | do-something-fast | do-something-slow | do-something-slowest | save-to-file   -  person Gregor y    schedule 30.07.2019
comment
Yup, sepakat bahwa pipeline perlu memiliki batasan, baik eksplisit maupun implisit (default, nilai tetap). Jadi, pertanyaannya: mungkin dengan pengaturan tentang ukuran buffer pipa, dll.? Sekarang, tampaknya Powershell memiliki Foreach Parallel tetapi hanya tersedia di Workflow.   -  person tkokasih    schedule 31.07.2019


Jawaban (1)


Saya tidak berpikir Anda akan dapat memanfaatkan saluran pipa dengan cara yang tidak sinkron seperti yang Anda inginkan tanpa harus mengeluarkan biaya yang mahal dalam penggunaan sumber daya. Saya mencoba menangkap semangat dari apa yang ingin Anda capai, tetapi dengan cara yang berbeda. Saya menggunakan contoh yang sedikit berbeda untuk mengilustrasikan cara kerja [Automation.PowerShell] Async.

#1. You have a list of room requests you want to process in a function.  TimeToClean was added as a controllable thread block.
$roomsToClean = @( ([psCustomObject]@{Name='Bedroom';TimeToClean=2}),
                   ([psCustomObject]@{Name='Kitchen';TimeToClean=5}),
                   ([psCustomObject]@{Name='Bathroom';TimeToClean=3}),
                   ([psCustomObject]@{Name='Living room';TimeToClean=1}),
                   ([psCustomObject]@{Name='Dining room';TimeToClean=1}),
                   ([psCustomObject]@{Name='Foyier';TimeToClean=1})
                )


#2. We will clean three rooms and return a custom PowerShell object with a message.
Function Clean-Room{
     param([string]$RoomName,[int]$Seconds)

      Sleep -Seconds $Seconds
      Write-Output [psCustomObject] @{Message= "The robot cleaned the $RoomName in $Seconds seconds."}    
}


#3. Executing this list synchronously will result in an approximate 13 second runtime.
Write-Host "===== Synchronous Results =====" -ForegroundColor Green

$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
foreach($item in $roomsToClean){
    $obj = Clean-Room $item.Name $item.TimeToClean
    Write-Output $obj.Message
}

$stopwatch.Stop()
Write-Host "Execution time for synchronous function was $($stopwatch.Elapsed)." -ForegroundColor Green


#4. Now let's run this function asynchronously for all of these items. Expected runtime will be approximately 5 seconds.


#=============== Setting up an ansynchronous powerShell Automation object and attaching it to a runspace.
#Many [Automation.PowerShell] objects can be attached to a given Runspace pool and the Runspace pool will manage queueing/dequeueing of each PS object as it completes.

#Create a RunSpace pool with 5 runspaces.  The pool will manage the 
#Many PowerShell autom
$minRunSpaces = 2
$maxRunsSpaces = 5
$runspacePool = [RunspaceFactory]::CreateRunspacePool($minRunSpaces, $maxRunsSpaces)    
$runspacePool.ApartmentState = 'STA'   #MTA = Multithreaded apartment  #STA = Singl-threaded apartment.
$runspacePool.Open()  #runspace pool must be opened before it can be used.

#For each room object, create an [Automation.PowerShell] object and attach it to the runspace pool.
#Asynchronously invoke the function for all objects in the collection.
$ps_collections = foreach($room in $roomsToClean){
                    try{

                    $ps = [System.Management.Automation.PowerShell]::Create() 
                    $ps.RunspacePool = $runspacePool

                    #Add your custom functions to the [Automation.PowerShell] object.
                    #Add argument with parameter name for readability. You may just use AddArgument as an alternative but know your positional arguments.

                    [void] $ps.AddScript(${function:Clean-Room})
                    [void] $ps.AddParameter('RoomName',$room.Name)        #Add parameterName,value
                    [void] $ps.AddParameter('Seconds',$room.TimeToClean)  #Add parameterName,value

                    #extend the ps management object to include AsyncResult and attach the AsyncResult object for receiving results at a later time.
                    $ps | Add-Member -MemberType NoteProperty -Name 'AsyncResult' -Value $ps.BeginInvoke()  #invoke asynchronously
                    $ps | Add-Member -MemberType ScriptMethod -Name 'GetAsyncResult' -Value {$this.EndInvoke($this.AsyncResult) } -PassThru
                    }
                    catch{
                        throw $_  #handle custom error here.
                    }
                }




#After the function has been asynchronously called for all room objects, Grab results from asynchronous function calls.
Write-Host "===== Asynchronous Results =====" -ForegroundColor Green
$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
foreach($ps in $ps_collections){
    $obj = $ps.GetAsyncResult()
    [void] $ps.Dispose()  #dispose of object after use.
    Write-Output $obj.Message
}

$stopwatch.Stop()
Write-Host "Execution time for asynchronous function was 
$($stopwatch.Elapsed)." -ForegroundColor Green

#Runspace cleanup.
If($runspacePool){
    [void] $runspacePool.Close()
    [void] $runspacePool.Dispose()
}

Waktu hasil akan sedikit berbeda tetapi akan terlihat seperti ini:

===== Synchronous Results =====

Robot membersihkan Kamar Tidur dalam 2 detik.

Robot membersihkan Dapur dalam 5 detik.

Robot membersihkan Kamar Mandi dalam 3 detik.

Robot membersihkan Ruang Tamu dalam 1 detik.

Robot membersihkan Ruang Makan dalam 1 detik.

Robot membersihkan Foyier dalam 1 detik.

Waktu eksekusi untuk fungsi sinkron adalah 00:00:13.0719157.

===== Asynchronous Results =====

Robot membersihkan Kamar Tidur dalam 2 detik.

Robot membersihkan Dapur dalam 5 detik.

Robot membersihkan Kamar Mandi dalam 3 detik.

Robot membersihkan Ruang Tamu dalam 1 detik. Robot membersihkan Ruang Makan dalam 1 detik.

Robot membersihkan Foyier dalam 1 detik.

Waktu eksekusi untuk fungsi asinkron adalah 00:00:04.9909951.

person binarySalt    schedule 21.04.2018