2010-10-25 27 views
94

Tengo un script de PowerShell para hacer un procesamiento por lotes en un grupo de imágenes y me gustaría hacer un procesamiento en paralelo. Powershell parece tener algunas opciones de procesamiento en segundo plano como start-job, wait-job, etc., pero el único recurso bueno que encontré para hacer trabajo paralelo fue escribir el texto de un script y ejecutar esos (PowerShell Multithreading)¿Pueden los comandos de ejecución de Powershell en paralelo?

Idealmente , me gustaría algo parecido a foreach paralelo en .NET 4.

algo muy virtuosa como:

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

Tal vez sería mejor simplemente dejar caer hacia abajo para C# ...

+0

** tl; dr: ** 'Receive-Job (esperar puesto de trabajo ($ a = Start-Job { "heyo ! "})); remove-job $ a' o '$ a = start-job {" heyo!"}; wait-job $ a; receive-job $ a; remove-job $ a' Tenga en cuenta también que si llama a' receive-job' antes de que el trabajo finalice, es posible que no obtenga nada en absoluto. – Andrew

+0

También '(get -job $ a) .jobstateinfo.state; ' – Andrew

Respuesta

71

Puede ejecutar trabajos paralelos en Powers infierno 2 usando Background Jobs. Consulte Start-Job y los cmdlets de otros trabajos.

# 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

Así que probé esta sugerencia varias veces, pero parece que mis variables no se están expandiendo correctamente. Para usar el mismo ejemplo, cuando esta línea se ejecuta:' Test-Path "\ \ $ _ \ c $ \ Something "' Esperaría que expanda '$ _' en el elemento actual. Sin embargo, no lo hace. En cambio, devuelve un valor vacío. Esto solo parece ocurrir desde bloques de script. Escribo ese valor inmediatamente después del primer comentario, parece que funciona correctamente. – rjg

+1

@likwid - parece una pregunta separada para el sitio –

+0

¿Cómo puedo ver la salida del trabajo que se ejecuta en segundo plano? – SimpleGuy

76

La respuesta de Steve Townsend es correcto en teoría, pero no en la práctica como @likwid señaló. Mi código revisado tiene en cuenta barrera de contexto de trabajo - ¡nada cruza esa barrera de forma predeterminada! La variable automática $_ se puede utilizar en el ciclo, pero no se puede usar directamente dentro del bloque de secuencia de comandos porque está dentro de un contexto separado creado por el trabajo.

para pasar variables del contexto padre al contexto de la niñez, utilice el parámetro -ArgumentList en Start-Job para enviar y utilizar param dentro del bloque de script para recibirlo.

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 * 

(que en general, como para proporcionar una referencia a la documentación PowerShell como elementos de prueba pero, por desgracia, mi búsqueda ha sido infructuosa. Si por casualidad usted conoce donde se documenta la separación contexto, publicar un comentario aquí que me deje saben!)

+0

Gracias por esta respuesta. Intenté usar tu solución, pero no pude hacerlo funcionar completamente. ¿Puedes echar un vistazo a mi pregunta aquí: http://stackoverflow.com/questions/28509659/unzipping-works-on-singlethread-but-not-multithread –

+0

Alternativamente, es bastante fácil invocar un archivo de script separado. Simplemente use 'Start-Job -FilePath script.ps1 -ArgumentList $ _' –

7

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

creé una invocación asíncrona que permite ejecutar varias secuencias de comandos qué bloques/cmdlets/funciones al mismo tiempo. esto es ideal para trabajos pequeños (exploración de subred o wmi contra 100 de máquinas) porque la sobrecarga para crear un espacio de ejecución frente al tiempo de inicio de la tarea inicial es bastante drástico. Se puede usar así.

con ScriptBlock,

$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 

solo cmdlet/función

$servers = Get-Content servers.txt 

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