28.3. 프로그램언어 고(Go)의 뮤텍스(Mutex) 활용법

프로그램언어 고(Go)에서의 뮤텍스 기본 사용법

제가 고(Go)언어에서 뮤텍스의 기본 사용법에 대해 설명 드리겠습니다.


package main

import (
    "fmt"
    "sync"
)

var count int
var mutex sync.Mutex

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            mutex.Lock()
            count++
            mutex.Unlock()
            wg.Done() 
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

위의 코드는 고루틴에서 안전하게 변수 count를 증가시키기 위해 뮤텍스를 사용한 기본 예제입니다.

먼저 sync 패키지에서 뮤텍스(Mutex)를 가져옵니다. 그리고 공유된 count 변수와 이를 보호할 뮤텍스 변수 mutex를 선언합니다.

main 함수에서 1000개의 고루틴을 launch합니다. 각 고루틴은 anonymouse 함수로 mutex.Lock()으로 뮤텍스를 잠그고(lock) count를 1 증가시킵니다. 그리고 나서 mutex.Unlock()으로 뮤텍스 잠금을 해제합니다(unlock).

이렇게함으로써 한번에 하나의 고루틴만이 lock된 critical section에 접근할 수 있고 race condition이 발생하지 않습니다.

마지막으로 모든 고루틴 작업이 완료될 때까지 기다렸다가(wg.Wait()) count 변수값을 출력합니다. 정상적으로 1000이 출력됩니다.

이 예제는 뮤텍스의 기본 사용법을 보여주고 있습니다. 뮤텍스를 사용하여 여러 고루틴에서 접근할 경우 race condition이 발생할 수 있는 critical section(복합체)를 보호하는 것이 중요합니다.

꼭 필요한 section에서만 잠시 lock을 걸고 사용 후 바로 unlock하는 것도 주의해야할 포인트입니다. 불필요하게 오랫동안 lock을 유지하면 다른 고루틴이 block되는 lock contention 현상이 발생할 수 있습니다.

이상으로 고(Go)언어의 뮤텍스 사용법에 대한 설명을 마치겠습니다. 이해가 가셨기를 바랍니다.

프로그램언어 고(Go)에서의 뮤텍스를 사용한 데이터 동기화

package main

import "sync"

// 공유 데이터
var data int

// 뮤텍스 생성
var mutex = &sync.Mutex{}

func updateData(n int) {
    // 뮤텍스 Lock
    mutex.Lock() 
    data += n
    // 뮤텍스 Unlock
    mutex.Unlock()
}

func main() {

    // 고루틴들 생성
    go updateData(1) 
    go updateData(2)
    go updateData(3)

    // 기다림
    time.Sleep(1 * time.Second)
    
    // 데이터 출력
    println(data)
}

Go 언어에서 뮤텍스(mutex)는 고루틴간에 안전하게 데이터를 공유하고 접근을 제어하는 데 사용됩니다.

위의 예제코드에서 보듯이, updateData 함수에서는 뮤텍스의 Lock 메서드를 호출하여 뮤텍스를 잠그고, Unlock 메서드를 호출하여 잠금을 해제합니다. 이를 통해 한 번에 하나의 고루틴만이 critical section에 접근할 수 있습니다.

이러한 방식으로 뮤텍스를 사용하면 데이터 경쟁 상태(race condition) 없이 안전하게 공유 데이터를 접근 및 수정할 수 있습니다.

여러 고루틴이 동시에 접근한다면 예측할 수 없는 결과가 발생할 수 있지만, 뮤텍스를 사용함으로써 이를 방지할 수 있습니다.

뮤텍스 사용 시 주의할 점은 deadlock 상황을 방지하기 위해 Unlock을 반드시 호출해야 한다는 것입니다.

이 외에도 RWMutex와Cond 등 보다 세밀한 제어가 필요한 경우가 있습니다.

항상 데이터의 특성과 고루틴의 특성을 고려하여 적합한 동기화 방법을 선택하는 것이 중요합니다.

이상으로 Go언어의 뮤텍스를 사용한 데이터 동기화에 대해 간략히 설명드렸습니다. 부족한 점이 있다면 양해 부탁드립니다.

프로그램언어 고(Go)에서의 뮤텍스와 원자성(Atomicity) 이해하기

프로그램 언어 Go에서 뮤텍스(mutex)는 여러 고루틴(goroutine)이 동시에 접근할 수 있는 임계 영역(critical section)을 보호하기 위해 사용합니다.

뮤텍스는 lock과 unlock 메서드를 제공합니다. lock 메서드를 호출하면 해당 뮤텍스를 획득하고, unlock 메서드를 호출하면 뮤텍스를 해제합니다.


import "sync"

var m sync.Mutex 
var balance int

func deposit(amount int) {
   m.Lock()
   balance += amount
   m.Unlock() 
}

위 예제에서 보는 것과 같이 임계 영역인 balance 변수 접근 코드를 뮤텍스로 감싸 동시 접근을 막았습니다.

고루틴이 lock 메서드를 호출하면 뮤텍스를 획득할 때까지 블로킹되므로, lock과 unlock은 항상 짝을 이뤄 사용해야 합니다.

Go 언어의 뮤텍스는 단일 고루틴만이 뮤텍스의 lock 섹션에 들어가도록 보장하는 상호 배제(mutual exclusion)를 제공합니다.

원자성(atomicity)은 연산 중에 다른 프로세스에 의해 중단되는 일 없이 모든 과정이 완료되어야 함을 의미합니다.

Go 언어의 sync/atomic 패키지는 이러한 원자성을 제공하기 위한 메서드를 제공합니다. 대표적인 메서드가 AddInt32, CompareAndSwapInt32 etc 입니다.


import "sync/atomic"

var counter uint64

func Increment() {
   atomic.AddUint64(&counter, 1) 
}

위 예제 코드에서 atomic.AddUint64 메서드를 사용하여 원자적으로 counter 변수 값을 1 증가시켰습니다.

이 메서드 사용 시 다른 고루틴의 간섭 없이 값 변경이 이루어지므로, 원자성을 보장받을 수 있습니다.

sync/atomic 패키지의 메서드들을 사용하면 lock/unlock이 필요 없이도 변수에 대한 원자 연산을 수행할 수 있어 효율적입니다.

이상으로 Go언어의 뮤텍스와 원자성에 대해 간략히 설명드렸습니다.

프로그램언어 고(Go)에서의 뮤텍스를 사용한 고루틴 동기화

Go 언어에서 고루틴은 경량 스레드와 비슷한 개념으로, 병행성 프로그래밍을 위해 사용됩니다. 여러 고루틴이 동시에 실행되면서 데이터를 공유하는 경우가 있는데, 이때 문제가 발생할 수 있습니다.

예를 들어 공유 데이터를 읽고 쓰는 두 개의 고루틴이 있다고 합시다. 한 고루틴이 값을 쓰고 있는 동안 다른 고루틴이 같은 데이터를 읽으면 데이터가 깨질 수 있습니다.

이를 방지하기 위해 Go언어에는 뮤텍스(Mutex)라는 동기화 기법이 있습니다. 뮤텍스를 사용하면 한 시점에 하나의 고루틴만 공유 데이터에 접근할 수 있게 제한합니다.


package main

import (
    "fmt"
    "sync"
)

var count int
var mutex = &sync.Mutex{}

func main() {

    for i := 0; i < 10; i++ {
        go increment()
    }

    time.Sleep(1 * time.Second)
    mutex.Lock()
    fmt.Println("count :", count)
    mutex.Unlock()

}

func increment() {
    mutex.Lock()
    count++
    mutex.Unlock()
} 

위 예제코드를 보면, count라는 전역변수를 여러 고루틴에서 읽고 쓰고 있습니다. 이때 mutex 뮤텍스를 사용하여 동시성 문제를 방지하고 있습니다.

increment() 함수가 호출되기 전에 mutex.Lock()으로 뮤텍스 잠금을 획득합니다. 이렇게 하면 다른 고루틴이 뮤텍스를 획득할 수 없기 때문에 count++ 연산을 안전하게 수행할 수 있습니다.

연산을 마친 후 mutex.Unlock()으로 뮤텍스 잠금을 해제합니다. 이제 다른 고루틴이 뮤텍스를 획득하고 critical section에 진입할 수 있게 됩니다.

이런 식으로 뮤텍스를 사용하여 고루틴들 간의 안전한 데이터 공유가 가능합니다.

프로그램언어 고(Go)에서의 뮤텍스와 채널의 차이 이해하기

프로그램언어 고(Go)에서 뮤텍스와 채널의 가장 큰 차이점은 동기화 방식에 있습니다.

뮤텍스는 상호배제를 기반으로 하는 동기화 방식을 사용합니다. 한 곳에서 뮤텍스를 잠그면, 다른 곳에서 락이 풀릴 때까지 기다려야 합니다. 이를 통해 데이터의 동기화를 보장할 수 있습니다.


// 뮤텍스 선언
var mutex = &sync.Mutex{} 

func UpdateData(data *Data) {
  mutex.Lock() // 뮤텍스 잠금
  defer mutex.Unlock() // 함수 종료 시 잠금 해제
  
  // data 변경 
} 

반면 채널(Channel)은 고루틴 간 통신을 위한 도구로, 데이터 전송 시 동기화를 지원합니다.
채널을 통해 데이터를 보내면 다른 고루틴이 데이터를 수신할때까지 대기합니다. 이를 통해 고루틴간 데이터 전달이 보장됩니다.


// 채널 선언
ch := make(chan int)  

func UpdateData(ch chan<- int) {
  // 채널로 데이터 전송
  ch <- 10 
}

func main() {
  UpdateData(ch)
  
  // 채널에서 데이터 수신
  data := <- ch
}

요약하자면, 뮤텍스는 데이터의 동기화를 위한 상호배제 락을 사용하고, 채널은 고루틴 간 통신을 위한 동기화 기능을 제공합니다.
뮤텍스가 전역적인 데이터 동기화에 유용하다면, 채널은 고루틴 간 통신 최적화에 효과적입니다.

Leave a Comment