Powershell Komutları Paralel Olarak Çalıştırabilir mi?


126

Bir grup görüntü üzerinde toplu işlem yapmak için bir powershell betiğim var ve bazı paralel işlemler yapmak istiyorum. Powershell, başlangıç ​​işi, iş bekle, vb. Gibi bazı arka plan işleme seçeneklerine sahip gibi görünüyor, ancak paralel çalışma yapmak için bulduğum tek iyi kaynak bir komut dosyasının metnini yazmak ve bunları çalıştırmaktı ( PowerShell Multithreading )

İdeal olarak, .net 4'teki paralel foreach'e benzer bir şey istiyorum.

Oldukça görünmeyen bir şey:

foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
   .. Do Work
}

Belki de c # 'a düşsem daha iyi olur ...


tl; dr: receive-job (wait-job ($a = start-job { "heyo!" })); remove-job $a veya $a = start-job { "heyo!" }; wait-job $a; receive-job $a; remove-job $aAyrıca receive-job, iş bitmeden ararsanız hiçbir şey alamayacağınızı da unutmayın.
Andrew

Ayrıca(get-job $a).jobstateinfo.state;
Andrew

Yanıtlar:


100

Arka Plan İşleri'ni kullanarak Powershell 2'de paralel işleri yürütebilirsiniz . Start-Job ve diğer job cmdlet'lerine göz atın .

# Loop through the server list
Get-Content "ServerList.txt" | %{

  # Define what each job does
  $ScriptBlock = {
    param($pipelinePassIn) 
    Test-Path "\\$pipelinePassIn\c`$\Something"
    Start-Sleep 60
  }

  # Execute the jobs in parallel
  Start-Job $ScriptBlock -ArgumentList $_
}

Get-Job

# Wait for it all to complete
While (Get-Job -State "Running")
{
  Start-Sleep 10
}

# Getting the information back from the jobs
Get-Job | Receive-Job

3
Bu yüzden bu öneriyi birkaç kez denedim, ancak görünen o ki değişkenlerim doğru şekilde genişletilmiyor. Aynı örneği kullanmak için, bu satır çalıştırıldığında: Geçerli öğeye Test-Path "\\$_\c$\Something"genişlemesini $_beklerim. Ancak öyle değil. Bunun yerine boş bir değer döndürür. Bu yalnızca komut dosyası bloklarının içinden gerçekleşiyor gibi görünüyor. Bu değeri ilk yorumdan hemen sonra yazarsam, doğru çalışıyor gibi görünüyor.
rjg

1
@likwid - site için ayrı bir soru gibi geliyor
Steve Townsend

Arka planda çalışan işin çıktısını nasıl görebilirim?
SimpleGuy

@SimpleGuy - çıktı yakalama hakkında bilgi için buraya bakın - stackoverflow.com/questions/15605095/… - arka plan işi tamamlanana kadar bunu güvenilir bir şekilde görüntüleyemezsiniz gibi görünmüyor.
Steve Townsend

@SteveTownsend Teşekkürler! Aslında çıktıyı görüntülemek ekranda o kadar iyi değil. Gecikmeli olarak geliyor, bu yüzden benim için kullanışlı değil. Bunun yerine yeni uçbirimde (kabuk) bir işlem başlattım, bu yüzden şimdi her işlem farklı uçbirimde çalışıyor, bu da ilerleme görünümünü çok daha iyi ve daha temiz veriyor.
SimpleGuy

98

Steve Townsend'in cevabı teoride doğrudur ancak @likwid'in belirttiği gibi pratikte değil. Revize edilmiş kodum iş bağlamı engelini hesaba katıyor - hiçbir şey varsayılan olarak bu engeli aşamaz! Otomatik $_değişken böylece döngüde kullanılabilir, ancak iş tarafından oluşturulan ayrı bir bağlam içinde olduğu için doğrudan kod bloğu içinde kullanılamaz.

Değişkenleri ana bağlamdan alt bağlama aktarmak için, -ArgumentListon parametresini Start-Jobkullanarak gönderin ve paramalmak için komut dosyası bloğunun içinde kullanın .

cls
# Send in two root directory names, one that exists and one that does not.
# Should then get a "True" and a "False" result out the end.
"temp", "foo" | %{

  $ScriptBlock = {
    # accept the loop variable across the job-context barrier
    param($name) 
    # Show the loop variable has made it through!
    Write-Host "[processing '$name' inside the job]"
    # Execute a command
    Test-Path "\$name"
    # Just wait for a bit...
    Start-Sleep 5
  }

  # Show the loop variable here is correct
  Write-Host "processing $_..."

  # pass the loop variable across the job-context barrier
  Start-Job $ScriptBlock -ArgumentList $_
}

# Wait for all to complete
While (Get-Job -State "Running") { Start-Sleep 2 }

# Display output from all jobs
Get-Job | Receive-Job

# Cleanup
Remove-Job *

(Genelde destekleyici kanıt olarak PowerShell belgelerine bir referans vermeyi severim, ancak ne yazık ki araştırmam sonuçsuz kaldı. Bağlam ayrımının nerede belgelendiğini biliyorsanız, bana bildirmek için buraya bir yorum gönderin!)


Bu cevap için teşekkürler. Çözümünüzü kullanmayı denedim, ancak tam olarak çalışmasını sağlayamadım. Soruma
David diyor Reinstate Monica

Alternatif olarak, ayrı bir komut dosyası çağırmak oldukça kolaydır. Sadece kullanınStart-Job -FilePath script.ps1 -ArgumentList $_
Chad Zawistowski

Alternatif bir yaklaşım, değişken genişletmeden başka hiçbir şeyin yapılmadığı komut dosyası oluşturmanın ön geçişini yapmak ve ardından oluşturulan komut dosyalarını paralel olarak çağırmaktır. Komut dosyası oluşturmaya uyarlanabilecek küçük bir aracım var, ancak hiçbir zaman komut dosyası oluşturmayı desteklemesi amaçlanmadı. Burada görebilirsiniz .
Walter Mitty

Bu çalışıyor. Ancak ScriptBlock'tan canlı yayın çıktı akışı alamıyorum. Çıktı yalnızca ScriptBlock döndüğünde yazdırılır.
vothaison

8

http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0

Aynı anda birden fazla komut dosyası bloğu / cmdlet / işlev çalıştırmanıza izin veren bir invoke-async oluşturdum. Bu, küçük işler için (alt ağ taraması veya 100'lerce makineye karşı wmi sorgusu) harikadır çünkü bir çalışma alanı yaratmanın ek yükü ve başlangıç ​​işinin başlama zamanı oldukça serttir. Bu şekilde kullanılabilir.

scriptblock ile,

$sb = [scriptblock] {param($system) gwmi win32_operatingsystem -ComputerName $system | select csname,caption} 

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $server -SetParam system  -ScriptBlock $sb

sadece cmdlet / işlev

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50

8

Bugünlerde buna pek çok cevap var:

  1. işler (veya PS 6 / 7'de veya modülde iş parçacığı)
  2. Başlangıç ​​süreci
  3. iş akışları
  4. başka bir çalışma alanına sahip powershell api
  5. hepsi localhost olabilen birden fazla bilgisayarla invoke-command (yönetici olması gerekir)
  6. ISE veya uzak powershell ISE sekmelerinde çoklu oturum (çalışma alanı) sekmeleri
  7. Powershell 7, foreach-object -parallel# 4'e bir alternatif sunuyor

İşte tam anlamıyla bir foreach-paralel ile iş akışları:

workflow work {
  foreach -parallel ($i in 1..3) { 
    sleep 5 
    "$i done" 
  }
}

work

3 done
1 done
2 done

Veya paralel bloklu bir iş akışı:

function sleepfor($time) { sleep $time; "sleepfor $time done"}

workflow work {
  parallel {
    sleepfor 3
    sleepfor 2
    sleepfor 1
  }
  'hi'
}

work 

sleepfor 1 done
sleepfor 2 done
sleepfor 3 done
hi

İşte çalışma alanlarına sahip bir api örneği:

$a =  [PowerShell]::Create().AddScript{sleep 5;'a done'}
$b =  [PowerShell]::Create().AddScript{sleep 5;'b done'}
$c =  [PowerShell]::Create().AddScript{sleep 5;'c done'}
$r1,$r2,$r3 = ($a,$b,$c).begininvoke() # run in background
$a.EndInvoke($r1); $b.EndInvoke($r2); $c.EndInvoke($r3) # wait
($a,$b,$c).streams.error # check for errors
($a,$b,$c).dispose() # clean

a done
b done
c done

7

Arka plan işlerinin kurulumu pahalıdır ve yeniden kullanılamaz. PowerShell MVP Oisin Grehan, PowerShell çoklu iş parçacığı için iyi bir örneğe sahiptir .

(25/10/2010 sitesi kapalı, ancak Web Arşivi aracılığıyla erişilebilir).

Burada bir veri yükleme rutininde kullanmak için uyarlanmış Oisin betiğini kullandım:

http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1


Bu cevap için bağlantı çürümesi başladı
Luke

4

Önceki cevapları tamamlamak Wait-Jobiçin, tüm işlerin tamamlanmasını beklemek için de kullanabilirsiniz :

For ($i=1; $i -le 3; $i++) {
    $ScriptBlock = {
        Param (
            [string] [Parameter(Mandatory=$true)] $increment
        )

        Write-Host $increment
    }

    Start-Job $ScriptBlock -ArgumentList $i
}

Get-Job | Wait-Job | Receive-Job

0

Powershell 7'de ForEach-Object -Parallel kullanabilirsiniz.

$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
    "$using:Message $_"
} -ThrottleLimit 4

0

En son çapraz platform powershell'i kullanıyorsanız (ki bunu btw yapmalısınız) https://github.com/powershell/powershell#get-powershell , &paralel komut dosyalarını çalıştırmak için tekli ekleyebilirsiniz . ( ;Sıralı olarak çalıştırmak için kullanın )

Benim durumumda paralel olarak 2 npm komut dosyası çalıştırmam gerekiyordu: npm run hotReload & npm run dev


Ayrıca powershell, komut dosyaları için kullanmak üzere npm'yi de kurabilirsiniz (varsayılan cmdolarak Windows'ta kullanır ).

Proje kök klasöründen çalıştırın: npm config set script-shell pwsh --userconfig ./.npmrc ve ardından tek npm komut dosyası komutunu kullanın:npm run start

"start":"npm run hotReload & npm run dev"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.