about_Thread_Jobs

간단한 설명

PowerShell 스레드 기반 작업에 대한 정보를 제공합니다. 스레드 작업은 현재 세션 프로세스 내의 별도 스레드에서 명령 또는 식을 실행하는 백그라운드 작업의 형식입니다.

자세한 설명

PowerShell은 작업을 통해 명령 및 스크립트를 동시에 실행합니다. 동시성을 지원하기 위해 PowerShell에서 제공하는 세 가지 작업 유형이 있습니다.

  • RemoteJob - 명령 및 스크립트는 원격 세션에서 실행됩니다. 자세한 내용은 about_Remote_Jobs 참조하세요.
  • BackgroundJob - 명령 및 스크립트는 로컬 컴퓨터에서 별도의 프로세스로 실행됩니다. 자세한 내용은 about_Jobs 참조하세요.
  • PSTaskJob 또는 ThreadJob - 명령 및 스크립트는 로컬 컴퓨터의 동일한 프로세스 내에서 별도의 스레드에서 실행됩니다.

스레드 기반 작업은 다른 스레드에서 동일한 프로세스에서 실행되기 때문에 원격 및 백그라운드 작업만큼 강력하지 않습니다. 한 작업에 프로세스와 충돌하는 심각한 오류가 있으면 프로세스의 다른 모든 작업이 종료됩니다.

그러나 스레드 기반 작업에는 오버헤드가 적습니다. 원격 계층 또는 serialization을 사용하지 않습니다. 결과 개체는 현재 세션에서 라이브 개체에 대한 참조로 반환됩니다. 이 오버헤드가 없으면 스레드 기반 작업이 더 빠르게 실행되고 다른 작업 유형보다 적은 리소스를 사용합니다.

Important

작업을 만든 부모 세션도 작업 상태 모니터링하고 파이프라인 데이터를 수집합니다. 작업이 완료된 상태에 도달하면 작업 자식 프로세스가 부모 프로세스에 의해 종료됩니다. 부모 세션이 종료되면 실행 중인 모든 자식 작업이 자식 프로세스와 함께 종료됩니다.

이 상황을 해결하는 방법에는 두 가지가 있습니다.

  1. 연결이 끊긴 세션에서 실행되는 작업을 만드는 데 사용합니다 Invoke-Command . 자세한 내용은 about_Remote_Jobs 참조하세요.
  2. 작업 대신 새 프로세스를 만드는 데 사용합니다 Start-Process . 자세한 내용은 Start-Process를 참조하세요.

스레드 기반 작업을 시작하고 관리하는 방법

스레드 기반 작업을 시작하는 방법에는 두 가지가 있습니다.

  • Start-ThreadJob- ThreadJob 모듈에서
  • ForEach-Object -Parallel -AsJob - 병렬 기능이 PowerShell 7.0에 추가되었습니다.

about_Jobs 설명된 동일한 작업 cmdlet을 사용하여 스레드 기반 작업을 관리합니다.

Start-ThreadJob 사용

ThreadJob 모듈은 PowerShell 6과 함께 처음 제공되었습니다. Windows PowerShell 5.1용 PowerShell 갤러리 설치할 수도 있습니다.

로컬 컴퓨터에서 스레드 작업을 시작하려면 중괄호({ })로 묶인 명령 또는 스크립트와 함께 cmdlet을 사용합니다Start-ThreadJob.

다음 예제에서는 로컬 컴퓨터에서 명령을 실행하는 Get-Process 스레드 작업을 시작합니다.

Start-ThreadJob -ScriptBlock { Get-Process }

Start-ThreadJob 명령은 실행 중인 작업을 나타내는 개체를 반환 ThreadJob 합니다. 작업 개체에는 현재 실행 중인 상태 포함하여 작업에 대한 유용한 정보가 포함되어 있습니다. 결과가 생성될 때 작업의 결과를 수집합니다.

ForEach-Object -Parallel -AsJob 사용

PowerShell 7.0은 cmdlet에 새 매개 변수 집합을 ForEach-Object 추가했습니다. 새 매개 변수를 사용하면 병렬 스레드에서 스크립트 블록을 PowerShell 작업으로 실행할 수 있습니다.

에 데이터를 파이프할 ForEach-Object -Parallel수 있습니다. 데이터는 병렬로 실행되는 스크립트 블록에 전달됩니다. 매개 변수는 -AsJob 각 병렬 스레드에 대한 작업 개체를 만듭니다.

다음 명령은 명령에 파이프된 각 입력 값에 대한 자식 작업이 포함된 작업을 시작합니다. 각 자식 작업은 파이프된 입력 값을 인수로 사용하여 명령을 실행 Write-Output 합니다.

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

ForEach-Object -Parallel 명령은 파이프된 PSTaskJob 각 입력 값에 대한 자식 작업이 포함된 개체를 반환합니다. 작업 개체에는 상태 실행 중인 자식 작업에 대한 유용한 정보가 포함되어 있습니다. 결과가 생성될 때 자식 작업의 결과를 수집합니다.

작업이 완료되고 작업 결과를 검색할 때까지 기다리는 방법

PowerShell 작업 cmdlet을 Wait-JobReceive-Job 사용하여 작업이 완료될 때까지 기다린 다음 작업에서 생성된 모든 결과를 반환할 수 있습니다.

다음 명령은 명령을 실행하는 Get-Process 스레드 작업을 시작한 다음 명령이 완료될 때까지 기다렸다가 마지막으로 명령에서 생성된 모든 데이터 결과를 반환합니다.

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

다음 명령은 파이프된 각 입력에 대한 명령을 실행하는 작업을 시작한 Write-Output 다음 모든 자식 작업이 완료될 때까지 기다렸다가 마지막으로 자식 작업에서 생성된 모든 데이터 결과를 반환합니다.

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

cmdlet은 Receive-Job 자식 작업의 결과를 반환합니다.

1
3
2
4
5

각 자식 작업이 병렬로 실행되므로 생성된 결과의 순서가 보장되지 않습니다.

스레드 작업 성능

스레드 작업은 다른 유형의 작업보다 빠르고 더 가볍습니다. 그러나 작업이 수행하는 작업과 비교할 때 여전히 클 수 있는 오버헤드가 있습니다.

PowerShell은 세션에서 명령 및 스크립트를 실행합니다. 한 세션에서 한 번에 하나의 명령이나 스크립트만 실행할 수 있습니다. 따라서 여러 작업을 실행할 때 각 작업은 별도의 세션에서 실행됩니다. 각 세션은 오버헤드에 기여합니다.

스레드 작업은 수행하는 작업이 작업을 실행하는 데 사용되는 세션의 오버헤드보다 클 때 최상의 성능을 제공합니다. 이 조건을 충족하는 두 가지 경우가 있습니다.

  • 작업은 계산 집약적입니다. 여러 스레드 작업에서 스크립트를 실행하면 여러 프로세서 코어를 활용하고 더 빠르게 완료할 수 있습니다.

  • 작업은 상당한 대기로 구성됩니다. I/O 또는 원격 호출 결과를 기다리는 데 시간을 소비하는 스크립트입니다. 일반적으로 병렬로 실행하면 순차적으로 실행하는 경우보다 더 빠르게 완료됩니다.

(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

위의 첫 번째 예제에서는 간단한 문자열 쓰기를 수행하는 1,000개의 스레드 작업을 만드는 foreach 루프를 보여줍니다. 작업 오버헤드로 인해 완료하는 데 36초가 넘게 걸립니다.

두 번째 예제에서는 cmdlet을 ForEach 실행하여 동일한 1000개 작업을 수행합니다. 이번에는 ForEach-Object 작업 오버헤드 없이 단일 스레드에서 순차적으로 실행됩니다. 단 7밀리초 만에 완료됩니다.

다음 예제에서는 10개의 개별 시스템 로그에 대해 최대 5,000개의 항목이 수집됩니다. 스크립트에는 여러 로그를 읽는 작업이 포함되므로 병렬로 작업을 수행하는 것이 좋습니다.

$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

스크립트는 작업이 병렬로 실행되는 시간의 절반으로 완료됩니다.

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

스레드 작업 및 변수

스레드 기반 작업에 값을 전달하는 방법에는 여러 가지가 있습니다.

Start-ThreadJob는 cmdlet에 파이프되거나, 키워드(keyword) 통해 $using 스크립트 블록에 전달되거나, 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는 변수로 파이프되고 키워드(keyword) 통해 스크립트 블록에 직접 전달되는 변수를 $using 허용합니다.

$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

스레드 작업은 동일한 프로세스에서 실행되므로 작업에 전달된 모든 변수 참조 형식을 신중하게 처리해야 합니다. 스레드로부터 안전한 개체가 아닌 경우 할당해서는 안 되며 메서드 및 속성은 스레드에서 호출되지 않아야 합니다.

다음 예제에서는 스레드로부터 안전한 .NET ConcurrentDictionary 개체를 모든 자식 작업에 전달하여 고유하게 명명된 프로세스 개체를 수집합니다. 스레드로부터 안전한 개체이므로 프로세스에서 작업이 동시에 실행되는 동안 안전하게 사용할 수 있습니다.

$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

참고 항목