>백엔드 개발 >Golang >Go의 동시성: 기본부터 고급 개념까지

Go의 동시성: 기본부터 고급 개념까지

Linda Hamilton
Linda Hamilton원래의
2024-10-03 06:11:30447검색

Concurrency in Go: From Basics to Advanced Concepts

목차

  1. 동시성 소개
  2. 동시성 대 병렬성
  3. Go-routines: 동시성의 구성 요소
  4. 채널: Go-routine 간의 통신
  5. Select 설명: 다중 채널 관리
  6. 동기화 기본 요소
  7. 동시성 패턴
  8. 컨텍스트 패키지: 취소 및 관리 관리 시간 초과.
  9. 모범 사례 및 일반적인 함정**

1.동시성 소개

동시성은 여러 작업을 동시에 처리하는 능력입니다. Go에서 동시성은 언어의 핵심 디자인에 내장된 일류 시민입니다. 동시성에 대한 Go의 접근 방식은 공유 메모리보다는 프로세스 간 통신을 강조하는 모델인 순차 프로세스 통신(CSP)을 기반으로 합니다.

2.동시성 대 병렬성:

Go-routine은 독립적으로 실행되는 프로세스의 구성인 동시성을 가능하게 합니다.
시스템에 여러 개의 CPU 코어가 있고 Go 런타임이 go 루틴을 병렬로 실행하도록 예약하는 경우 병렬 처리(동시 실행)가 발생할 수 있습니다.

3. 이동 루틴:
동시성의 빌딩 블록은 Go 루틴이 Go 런타임에 의해 관리되는 경량 스레드입니다. 다른 함수나 메서드와 동시에 실행되는 함수나 메서드입니다. Go 루틴은 Go 동시성 모델의 기초입니다.

주요 특징:

  • 경량: Go 루틴은 OS 스레드보다 훨씬 가볍습니다. 성능에 큰 영향을 주지 않고 수천 개의 go-routine을 쉽게 생성할 수 있습니다.
  • Go 런타임에 의해 관리됨: Go 스케줄러는 사용 가능한 OS 스레드 전반에 걸쳐 go 루틴 배포를 처리합니다.
  • 저렴한 생성: go 루틴을 시작하는 것은 함수 호출 전에 go 키워드를 사용하는 것만큼 간단합니다.
  • 스택 크기: Go 루틴은 필요에 따라 늘리거나 줄일 수 있는 작은 스택(약 2KB)으로 시작합니다.

고루틴 만들기:
go-routine을 시작하려면 go 키워드와 함수 호출을 차례로 사용하면 됩니다.

go functionName()

또는 익명 함수 사용:

go func() {
    // function body
}()

고루틴 일정:

  • Go 런타임은 M:N 스케줄러를 사용하며, 여기서 M go 루틴은 N OS 스레드에 예약됩니다.
  • 이 스케줄러는 비선점형입니다. 즉, 유휴 상태이거나 논리적으로 차단된 경우 go-routine이 제어권을 양보합니다.

통신 및 동기화:

  • 고루틴은 일반적으로 "메모리 공유로 통신하지 말고 통신으로 메모리 공유" 원칙을 준수하여 채널을 사용하여 통신합니다.
  • 간단한 동기화의 경우 sync.WaitGroup 또는 sync.Mutex와 같은 기본 요소를 사용할 수 있습니다.

설명 예시:

package main

import (
    "fmt"
    "time"
)

func printNumbers() {
    for i := 1; i <= 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Printf("%d ", i)
    }
}

func printLetters() {
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(150 * time.Millisecond)
        fmt.Printf("%c ", i)
    }
}

func main() {
    go printNumbers()
    go printLetters()
    time.Sleep(2 * time.Second)
    fmt.Println("\nMain function finished")
}

설명:

  • printNumbers와 printLetters라는 두 가지 함수를 정의합니다.
  • 기본적으로 go 키워드를 사용하여 이러한 기능을 고루틴으로 시작합니다.
  • 그런 다음 주 함수는 고루틴이 완료될 수 있도록 2초 동안 휴면 상태로 유지됩니다.
  • 고루틴이 없으면 이러한 기능은 순차적으로 실행됩니다. 고루틴을 사용하면 동시에 실행됩니다.
  • 출력에는 숫자와 문자가 인터리브되어 표시되어 동시 실행을 보여줍니다.

고루틴 수명 주기:

  • 고루틴은 go 키워드로 생성되면 시작됩니다.
  • 기능이 완료되거나 프로그램이 종료되면 종료됩니다.
  • 고루틴은 제대로 관리하지 않으면 유출될 수 있으므로 종료할 수 있는지 확인하는 것이 중요합니다.

모범 사례:

  • 라이브러리에서 고루틴을 만들지 마세요. 호출자가 동시성을 제어하도록 합니다.
  • 고루틴을 무제한으로 생성할 때는 주의하세요.
  • 채널이나 동기화 프리미티브를 사용하여 고루틴 간을 조정하세요.
  • 여러 고루틴을 효율적으로 관리하려면 작업자 풀을 사용하는 것을 고려해 보세요.

고루틴 설명이 포함된 간단한 예

package main

import (
    "fmt"
    "time"
)

// printNumbers is a function that prints numbers from 1 to 5
// It will be run as a goroutine
func printNumbers() {
    for i := 1; i <= 5; i++ {
        time.Sleep(500 * time.Millisecond) // Sleep for 500ms to simulate work
        fmt.Printf("%d ", i)
    }
}

// printLetters is a function that prints letters from 'a' to 'e'
// It will also be run as a goroutine
func printLetters() {
    for i := 'a'; i <= 'e'; i++ {
        time.Sleep(300 * time.Millisecond) // Sleep for 300ms to simulate work
        fmt.Printf("%c ", i)
    }
}

func main() {
    // Start printNumbers as a goroutine
    // The 'go' keyword before the function call creates a new goroutine
    go printNumbers()

    // Start printLetters as another goroutine
    go printLetters()

    // Sleep for 3 seconds to allow goroutines to finish
    // This is a simple way to wait, but not ideal for production code
    time.Sleep(3 * time.Second)

    // Print a newline for better formatting
    fmt.Println("\nMain function finished")
}

4.채널 :

채널은 go 루틴이 서로 통신하고 실행을 동기화할 수 있게 해주는 Go의 핵심 기능입니다. 이는 하나의 go-routine이 다른 go-routine으로 데이터를 보내는 방법을 제공합니다.

채널의 목적

Go의 채널은 두 가지 주요 용도로 사용됩니다.
a) 통신: 고루틴이 서로 값을 보내고 받을 수 있도록 합니다.
b) 동기화: 고루틴 전체에서 실행을 동기화하는 데 사용할 수 있습니다.

생성: 채널은 make 기능을 사용하여 생성됩니다.

ch := make(chan int)  // Unbuffered channel of integers

보내기: 값은 <- 연산자를 사용하여 채널로 전송됩니다:

ch <- 42  // Send the value 42 to the channel

Receiving: Values are received from a channel using the <- operator:

value := <-ch  // Receive a value from the channel

Types of Channels

a) Unbuffered Channels:

  • Created without a capacity: ch := make(chan int)
  • Sending blocks until another goroutine receives.
  • Receiving blocks until another goroutine sends.
ch := make(chan int)
go func() {
    ch <- 42  // This will block until the value is received
}()
value := <-ch  // This will receive the value

b) Buffered Channels:

  • Created with a capacity: ch := make(chan int, 3)
  • Sending only blocks when the buffer is full.
  • Receiving only blocks when the buffer is empty.
ch := make(chan int, 2)
ch <- 1  // Doesn't block
ch <- 2  // Doesn't block
ch <- 3  // This will block until a value is received

Channel Directions

Channels can be directional or bidirectional:

  • Bidirectional: chan T
  • Send-only: chan<- T
  • Receive-only: <-chan T

Example :

func send(ch chan<- int) {
    ch <- 42
}

func receive(ch <-chan int) {
    value := <-ch
    fmt.Println(value)
}

Closing Channels

Channels can be closed to signal that no more values will be sent:

close(ch)

Receiving from a closed channel:

If the channel is empty, it returns the zero value of the channel's type.
You can check if a channel is closed using a two-value receive:

value, ok := <-ch
if !ok {
    fmt.Println("Channel is closed")
}

Ranging over Channels

You can use a for range loop to receive values from a channel until it's closed:

for value := range ch {
    fmt.Println(value)
}

Hey, Thank you for staying until the end! I appreciate you being valuable reader and learner. Please follow me here and also on my Linkedin and GitHub .

위 내용은 Go의 동시성: 기본부터 고급 개념까지의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.