Go 동시성 프로그래밍: 원자 카운터 (Atomic Counters)

Beginner

This tutorial is from open-source community. Access the source code

소개

이 랩은 여러 고루틴에서 접근하는 원자 카운터 (atomic counters) 를 위해 sync/atomic 패키지를 사용하여 Go 에서 상태를 관리하는 데 중점을 둡니다.

원자 카운터 (Atomic Counters)

문제는 50 개의 고루틴과 sync/atomic 패키지를 사용하여 카운터를 정확히 1000 번 증가시키는 것입니다.

  • sync/atomic 패키지를 사용하여 카운터를 증가시킵니다.
  • WaitGroup 을 사용하여 모든 고루틴이 작업을 완료할 때까지 기다립니다.
## 우리는 정확히 50,000 번의 연산을 얻을 것으로 예상합니다. 만약
## 원자적이지 않은 `ops++` 를 사용하여 카운터를 증가시켰다면,
## 고루틴들이 서로 간섭하기 때문에
## 실행마다 다른 숫자를 얻을 가능성이 높습니다. 또한, `-race` 플래그로 실행할 때
## 데이터 경합 오류가 발생할 것입니다.
$ go run atomic-counters.go
ops: 50000

## 다음으로, 상태 관리를 위한 또 다른 도구인 뮤텍스 (mutex) 를 살펴보겠습니다.

전체 코드는 다음과 같습니다.

// Go 에서 상태를 관리하는 주요 메커니즘은
// 채널을 통한 통신입니다. 예를 들어
// [작업자 풀](worker-pools) 에서 이를 보았습니다. 하지만 상태를 관리하는 몇 가지 다른
// 옵션도 있습니다. 여기서는 여러 고루틴에서 접근하는 _원자 카운터_를 위해 `sync/atomic` 패키지를 사용하는 방법을
// 살펴보겠습니다.

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {

	// (항상 양수인) 카운터를 나타내기 위해 부호 없는 정수를 사용합니다.
	var ops uint64

	// WaitGroup 은 모든 고루틴이
	// 작업을 완료할 때까지 기다리는 데 도움이 됩니다.
	var wg sync.WaitGroup

	// 각 카운터를 정확히 1000 번 증가시키는 50 개의 고루틴을 시작합니다.
	for i := 0; i < 50; i++ {
		wg.Add(1)

		go func() {
			for c := 0; c < 1000; c++ {
				// 카운터를 원자적으로 증가시키기 위해
				// `AddUint64` 를 사용하고, `&` 구문을 사용하여
				// `ops` 카운터의 메모리 주소를 제공합니다.
				atomic.AddUint64(&ops, 1)
			}
			wg.Done()
		}()
	}

	// 모든 고루틴이 완료될 때까지 기다립니다.
	wg.Wait()

	// 다른 고루틴이 쓰고 있지 않다는 것을 알기 때문에
	// 이제 `ops` 에 안전하게 접근할 수 있습니다. 업데이트되는 동안
	// 원자성을 안전하게 읽는 것도
	// `atomic.LoadUint64` 와 같은 함수를 사용하여 가능합니다.
	fmt.Println("ops:", ops)
}

요약

이 랩에서는 여러 고루틴을 사용하여 카운터를 증가시켜 Go 에서 상태를 관리하기 위해 sync/atomic 패키지를 사용하는 방법을 배웠습니다. AddUint64 함수는 카운터를 원자적으로 증가시키는 데 사용되었고, WaitGroup 은 모든 고루틴이 작업을 완료할 때까지 기다리는 데 사용되었습니다.