자바스크립트를 활성화 해주세요

p038 Pwsh Core에 도입된 ThreadJob에 대하여

 ·  ☕ 4 min read

윈도우즈의 PowerShell에서는 Start-Job 등의 명령어를 사용해 무거운 처리를 배경으로 실행시키는 Job 시스템이 제공되어 왔습니다만,
PowerShell Core 6.0부터는 Start-Job 대신 & 를 사용할 수도 있게 되었습니다.

이 Job 구조는 개개의 Job을 PowerShell의 프로세스로서 실행하는 것이기 때문에 Job의 독립성은 뛰어나지만 복수의 Job을 동시에 병렬해서 실행하는 경우는 프로세스 기동의 코스트의 높이에 따라 퍼포먼스가 급격히 나빠져 버리는 문제를 안고 있었습니다.

예를 들어 다음과 같은 단순한 Job이라도 10개의 병렬로 실행하면 비정상적으로 시간이 걸려 버립니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Measure-Command {
    1..10 | ForEach-Object {
        Start-Job -ArgumentList $_ -ScriptBlock { 
            param($arg) 
            Write-Output "arg = $arg" 
            Start-Sleep -s 20
        }
    }
    Get-Job | Receive-Job -Wait -OutVariable Output
}
Get-Job | Select-Object Id, Name, PSBeginTime, PSEndTime

결과

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
PS C:\Users\Administrator\blog>     1..10 | ForEach-Object {
>>         Start-Job -ArgumentList $_ -ScriptBlock {
>>             param($arg)
>>             Write-Output "arg = $arg"
>>             Start-Sleep -s 20
>>         }
>>     }

Id     Name            PSJobTypeName   State         HasMoreData     Location             Comman
                                                                                          d
--     ----            -------------   -----         -----------     --------             ------
1      Job1            BackgroundJob   Running       True            localhost             ...
3      Job3            BackgroundJob   Running       True            localhost             ...
5      Job5            BackgroundJob   Running       True            localhost             ...
7      Job7            BackgroundJob   Running       True            localhost             ...
9      Job9            BackgroundJob   Running       True            localhost             ...
11     Job11           BackgroundJob   Running       True            localhost             ...
13     Job13           BackgroundJob   Running       True            localhost             ...
15     Job15           BackgroundJob   Running       True            localhost             ...
17     Job17           BackgroundJob   Running       True            localhost             ...
19     Job19           BackgroundJob   Running       True            localhost             ...


PS C:\Users\Administrator\blog>     Get-Job | Receive-Job -Wait -OutVariable Output
arg = 1
arg = 2
arg = 3
arg = 4
arg = 7
arg = 5
arg = 6
arg = 9
arg = 10
arg = 8

참고로 이 처리를 실행하고 있을 때는 이런 느낌으로 대량의 PowerShell 프로세스가 기동되어 자원을 소비하고 있음을 알 수 있습니다.

p038_start_job_process.png

ThreadJob

이 문제를 해소하기 위해서 도입된 것이 ThreadJob입니다.

이 모듈은은 Paul Higin씨 가 작성했는데, PowerShell Core 6.1 Preview.4 부터는 표준으로 편입되고 있습니다.

기존의 Job과 다른 것은 PSJob TypeName 이 ThreadJob라고 표기 되는 정도인 듯 합니다.

ThreadJob의 구조

ThreadJob에서는 각각의 Job은 별도의 프로세스가 되지 않고 동일 프로세스 내에서 Runspace를 나누는 것으로 이루어지고 있습니다.

그러므로 Process는 하나만 생성됩니다.

ThreadJob은 이 병렬처리의 방법을 PowerShell Job의 인터페이스에 맞춘 것입니다.

https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/remoting/client/Job2.cs

성능차

ThreadJob은 동일 프로세스 내의 별도의 Runspace에서 실행되기 때문에 기존 Job만큼 독립성은 없지만 기동비용이 적어 여러 Job을 병렬 실행하는데 적합합니다.

처음에 나온 Task의 예를 ThreadJob에서 시험해 보면 약 0.3초와 1초도 걸리지 않고 끝나 버립니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Measure-Command {
    1..10 | ForEach-Object {
        Start-ThreadJob -ArgumentList $_ -ScriptBlock { 
            param($arg) 
            Write-Output "arg = $arg" 
        }
    }
    Get-Job | Receive-Job -Wait -OutVariable Output
}
Get-Job | Select-Object Id, Name, PSBeginTime, PSEndTime

Windows PowerShell에서의 Thread Job 이용

이 TreadJob은 모듈로 제공되기 때문에 PowerShell Core 뿐만 아니라 Windows PowerShell(PowerShell 3.0 이후)에서도 이용 가능합니다.

설치방법은 여러가지가 있는데 Install-Module을 하는게 가장 빠를 것 같습니다.

1
Install-Module ThreadJob-Scope CurrentUser

위의 예제도 Windows PowerShell 5.1 에서도 문제없이 실행됩니다.

기존의 Job과 ThreadJob의 사용 구분

마지막으로 기존의 Job과 ThreadJob의 사용구분에 대해 간단히 개인적인 의견을 얘기하면 다음과 같습니다.

이번 포스트의 예에서 보았듯이 단순한 처리시간만 놓고 보면 ThreadJob만 사용하면 될 것 같습니다.
성능은 중요한 요소이기 때문에 Thread Job을 사용할 수 있는 환경에서는 적극적으로 ThreadJob을 사용하는 것이 좋습니다.

다만, 기존의 Job에도 메리트는 있습니다.

  1. 개개의 Job이 별도의 프로세스이기 때문에 만일 Job 내부에서 예기치 않은 예외가 발생했을 경우라도 부모 Process에 영향을 미치는 일은 결코 없습니다.
  2. 또 예를 들어 메모리와 같은 자원을 대량으로 소비하는 처리를 Job으로 한 경우 부모 Process에 해당 자원 소비가 영향을 주는 일은 없으며 Job이 종료되면 프로세스 자체가 사라지기 때문에 자원의 Release 를 잊어서 발생하는 Memory Leak과 같은 것을 걱정할 필요가 전혀 없습니다.

ThreadJob 에서는 RunSpace가 나누어져 있어도 같은 Process이기 때문에 예외의 종류에 따라서는 프로세스 자체가 빠질 가능성이 있고 리소스 소비나 Release Leak에 대해서도 유의해야 합니다.

실행하는 Job의 내용에 따라 기존 Job의 이점을 얻을 수 있으면 그것을 이용하는 것이 좋습니다.

아니면 단순하게

무거운 처리에는 종래의 Job, 대량의 병렬 처리에는 ThreadJob.

정도로 구분해도 될 것 같습니다.

레퍼런스

공유하기

tkim
글쓴이
tkim
Software Engineer