Goroutine에서 os.Chdir()
사용할 때 발생하는 문제와 해결 방법
안녕하세요, 여러분.
오늘은 Go 언어에서 Goroutine을 사용할 때 주의해야 할 중요한 사항 중 하나인 os.Chdir()
함수에 대해 이야기해보려 합니다.
os.Chdir()
는 현재 작업 디렉토리를 변경하는 함수인데, 이를 Goroutine과 함께 사용하면 예상치 못한 문제가 발생할 수 있습니다.
이 강의에서는 그 이유와 해결 방법을 알아보겠습니다.
1. Goroutine과 os.Chdir()
의 충돌
Goroutine은 Go 언어에서 동시성을 처리하는 매우 효과적인 방법인데요, 여러 Goroutine이 동시에 실행되면서 서로 다른 작업을 할 수 있습니다.
그런데 Goroutine에서 os.Chdir()
를 호출하면 문제가 발생할 수 있습니다.
그 이유를 보기 위해 간단한 예제를 살펴보겠습니다.
package main
import (
"fmt"
"os"
"path/filepath"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
baseDir, _ := os.Getwd()
wg.Add(3)
for i := 1; i < 4; i++ {
go changeDirAndPrint(baseDir, i)
}
wg.Wait()
}
func changeDirAndPrint(baseDir string, num int) {
defer wg.Done()
wd, _ := os.Getwd()
fmt.Printf("Goroutine %d: 시작 디렉토리: %s\n", num, wd)
os.Chdir(filepath.Join(baseDir, fmt.Sprintf("%d", num)))
wd, _ = os.Getwd()
fmt.Printf("Goroutine %d: 변경 후 디렉토리: %s\n", num, wd)
time.Sleep(1 * time.Second)
wd, _ = os.Getwd()
fmt.Printf("Goroutine %d: 깨어난 후 디렉토리: %s\n", num, wd)
}
위 코드는 각 Goroutine이 각각 다른 디렉토리로 이동한 뒤, 디렉토리 상태를 출력하는 예제입니다.
하지만 실행 결과는 예상과 다르게 모든 Goroutine이 마지막에 같은 디렉토리에 있는 것처럼 동작하는데요.
실행 결과:
Goroutine 1: 시작 디렉토리: /Users/example/work
Goroutine 2: 시작 디렉토리: /Users/example/work
Goroutine 3: 시작 디렉토리: /Users/example/work
Goroutine 1: 변경 후 디렉토리: /Users/example/work/1
Goroutine 2: 변경 후 디렉토리: /Users/example/work/2
Goroutine 3: 변경 후 디렉토리: /Users/example/work/3
Goroutine 1: 깨어난 후 디렉토리: /Users/example/work/3
Goroutine 2: 깨어난 후 디렉토리: /Users/example/work/3
Goroutine 3: 깨어난 후 디렉토리: /Users/example/work/3
2. 문제의 원인: os.Chdir()
는 프로세스 전역 상태 변경
이 문제의 원인은 Go의 Goroutine이 프로세스 내에서 실행된다는 점에 있습니다.
즉, Goroutine은 같은 프로세스의 자원을 공유하는데, 그 중 하나가 현재 작업 디렉토리입니다.
os.Chdir()
는 프로세스 전역 상태인 현재 작업 디렉토리를 변경하는 함수이기 때문에, 한 Goroutine에서 디렉토리를 변경하면 다른 Goroutine에도 영향을 미치게 됩니다.
Goroutine은 하나의 프로세스에서 여러 개의 경량 스레드처럼 실행되므로, 프로세스 단위로 공유되는 자원(예: 현재 작업 디렉토리)은 독립적으로 유지되지 않습니다.
따라서 마지막에 실행된 Goroutine의 디렉토리 변경이 모든 Goroutine에 적용된 것입니다.
3. 해결 방법: os/exec.Cmd
로 독립 프로세스 실행
그렇다면 Goroutine이 각자 독립적으로 다른 디렉토리에서 작업을 수행하도록 하려면 어떻게 해야 할까요?
그 해결책은 os/exec.Cmd
를 사용하는 것입니다.
os/exec.Cmd
는 외부 명령을 실행할 때 새로운 프로세스를 생성하여, 각 프로세스가 독립적인 작업 디렉토리를 가질 수 있게 해줍니다.
다음 코드는 os/exec.Cmd
를 사용하여 각 Goroutine이 독립적인 디렉토리에서 작업을 수행하도록 한 예시입니다.
package main
import (
"fmt"
"os/exec"
"path/filepath"
"sync"
)
var wg sync.WaitGroup
func main() {
baseDir := "/path/to/base"
wg.Add(3)
for i := 1; i < 4; i++ {
go runCommandInDir(baseDir, i)
}
wg.Wait()
}
func runCommandInDir(baseDir string, num int) {
defer wg.Done()
cmd := exec.Command("ls")
cmd.Dir = filepath.Join(baseDir, fmt.Sprintf("%d", num))
output, err := cmd.Output()
if err != nil {
fmt.Printf("Goroutine %d: 에러 발생 - %v\n", num, err)
return
}
fmt.Printf("Goroutine %d: 출력 결과:\n%s\n", num, output)
}
이 코드는 각 Goroutine이 독립적으로 ls
명령을 실행하며, cmd.Dir
을 통해 각 프로세스의 작업 디렉토리를 개별적으로 설정합니다.
이렇게 하면 각 Goroutine이 독립된 디렉토리에서 작업을 수행할 수 있습니다.
4. 요약: Goroutine에서 os.Chdir()
사용 시 주의사항
os.Chdir()
는 프로세스 전역 상태를 변경합니다. Goroutine은 같은 프로세스 내에서 실행되므로, 한 Goroutine에서 디렉토리를 변경하면 다른 Goroutine에도 영향을 미칩니다.- Goroutine마다 독립적인 디렉토리에서 작업을 수행하려면,
os/exec.Cmd
를 사용하여 각 작업을 별도의 프로세스로 실행하는 것이 좋습니다. - Goroutine이 서로 다른 디렉토리에서 처리해야 할 작업이 있을 때는, 프로세스 분리가 가장 안전한 접근 방식입니다.
이 강의를 통해 Goroutine과 os.Chdir()
사용 시 발생할 수 있는 문제와 그 해결 방법을 배웠습니다.
Go 언어의 동시성 처리에서 이러한 전역 상태의 공유는 주의 깊게 다뤄야 하는 부분입니다.
여러분도 Goroutine을 사용할 때 이러한 점을 염두에 두고 프로세스 자원을 적절하게 관리하시길 바랍니다.
감사합니다.
'Go' 카테고리의 다른 글
Go 언어의 리플렉션, 제대로 활용하는 방법! (1) | 2024.10.30 |
---|---|
Go 언어의 `:=` 연산자, 그 숨겨진 이야기! (1) | 2024.10.30 |
Go 1.18에서 `any`로 더 간결하게: `interface{}`의 진화 (0) | 2024.10.06 |
Go 언어 `reflect` 패키지 완벽 가이드: 런타임 타입 처리를 마스터하자 (0) | 2024.10.06 |
Go 인터페이스의 모든 것: 내부 구조와 동작 원리 분석 (0) | 2024.10.06 |