Stdout에 대한 동시 쓰기: 스레드 안전성 분석
최근 토론에서 스레드에 대한 논쟁을 촉발한 Go 코드 조각이 발표되었습니다. stdout에 동시에 쓸 때 안전합니다. 문제의 코드는 다음과 같습니다.
<code class="go">package main import ( "fmt" "os" "strings" ) func main() { x := strings.Repeat(" ", 1024) go func() { for { fmt.Fprintf(os.Stdout, x+"aa\n") } }() go func() { for { fmt.Fprintf(os.Stdout, x+"bb\n") } }() go func() { for { fmt.Fprintf(os.Stdout, x+"cc\n") } }() go func() { for { fmt.Fprintf(os.Stdout, x+"dd\n") } }() <-make(chan bool) }</code>
스레드 안전 고려 사항:
여러 고루틴이 동시에 stdout에 쓸 때 이 코드가 스레드로부터 안전한지 여부에 대한 의문이 생깁니다. 이 문제에 대해 다양한 출처와 의견이 거론됐지만 명확한 답을 찾지 못했다. 주제를 더 자세히 살펴보겠습니다.
fmt 패키지 동작:
fmt 패키지 함수는 단순히 io.Writer 구현을 가져와서 Write()를 호출합니다. 함수 자체는 스레드로부터 안전합니다. 즉, fmt.F* 함수에 대한 여러 동시 호출이 안전하다는 의미입니다. 그러나 stdout에 대한 동시 쓰기 구현은 사용된 특정 "작성기"에 따라 다릅니다.
"작성기" 구현:
"작성기"의 두 가지 주요 범주가 관련됩니다. :
POSIX 의미:
파일 설명자의 경우 POSIX에서는 쓰기가 필요합니다. (2) 일반 파일이나 심볼릭 링크에서 작동할 때 원자성을 호출합니다. 이는 stdout이 파일 설명자로 간주되는 경우 쓰기 호출이 원자성이어야 함을 의미합니다.
Go 표준 라이브러리 구현:
Go 표준 라이브러리의 파일 설명자와 소켓 주위의 래퍼는 쓰기 작업을 기본 객체에 1:1로 매핑하도록 설계되었습니다. 이렇게 하면 쓰기 호출이 분할되거나 서로 결합될 가능성이 제거됩니다.
결론:
사용 가능한 정보와 POSIX write(2) 호출의 기본 의미론을 기반으로 합니다. , 제공된 코드는 데이터 경합의 대상이 아닙니다. 그러나 기본 파일 설명자에 기록된 출력은 예측할 수 없는 순서로 혼합될 수 있습니다. 이 동작은 OS 커널 버전, Go 버전, 하드웨어 및 시스템 로드와 같은 요소의 영향을 받습니다.
각 특정 fmt.Fprint* 호출의 출력이 결과 출력에서 연속된 부분으로 표시되도록 하려면 , 잠금을 사용하거나 자체 잠금 메커니즘을 제공하는 로그 패키지를 사용하여 호출을 직렬화하는 것이 좋습니다.
위 내용은 Go에서 `stdout`에 동시 쓰기가 스레드로부터 안전합니까? `fmt.Fprintf` 동작에 대한 자세한 분석.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!