Параллелизм — это способность программы выполнять несколько задач одновременно, что потенциально увеличивает общее время выполнения. В Go параллелизм достигается с помощью горутин и каналов.

Горутины — это легкие потоки, которые позволяют функциям выполняться одновременно. Они создаются с использованием ключевого слова go и запускаются одновременно с другими горутинами.

Каналы используются для связи и синхронизации между горутинами. Они позволяют одной горутине отправлять данные другой горутине и блокировать ее до тех пор, пока данные не будут получены.

Есть несколько способов добавить параллелизм в Go, в том числе:

I. Использование горутин: одновременный запуск функций с использованием ключевого слова go.

func main() {
    go doSomething()
    go doSomethingElse()

    // Wait for goroutines to finish executing
    time.Sleep(time.Second)
}

func doSomething() {
    // Do something
}

func doSomethingElse() {
    // Do something else
}

В приведенном выше примере функции doSomething и doSomethingElse выполняются одновременно с использованием горутин.

В Go горутина — это облегченный поток выполнения, которым управляет среда выполнения Go. Горутины позволяют одновременно выполнять несколько функций в одном и том же адресном пространстве без необходимости явного управления потоками или координации.

Горутины создаются с использованием ключевого слова go, за которым следует функция, которая должна выполняться одновременно. Когда горутина создается, она планируется для запуска на доступном логическом процессоре, который управляется планировщиком времени выполнения Go. Планировщик управляет выделением логических процессоров горутинам и обеспечивает их справедливое и эффективное выполнение.

II. Использование каналов: синхронизация связи между горутинами с использованием каналов.

func main() {
    c := make(chan int)

    go send(c)
    go receive(c)

    // Wait for goroutines to finish executing
    time.Sleep(time.Second)
}

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

func receive(c <-chan int) {
    fmt.Println(<-c)
}

В приведенном выше примере функции send и receive взаимодействуют с помощью канала. sync.WaitGroup используется для координации выполнения нескольких горутин.

Каналы в Go — это тип механизма связи, который позволяет программам синхронизировать и обмениваться данными безопасным и контролируемым образом. Каналы используются для отправки и получения данных между горутинами и определяются с использованием ключевых слов make и chan для указания типа данных, которые будут переданы.

В Go есть два типа каналов: небуферизованные каналы и буферизованные каналы.

Небуферизованные каналы блокируются до тех пор, пока данные не будут получены приемником. Когда значение отправляется на небуферизованный канал, отправитель блокируется до тех пор, пока получатель не получит значение. Точно так же, когда получатель пытается получить значение из небуферизованного канала, он блокируется до тех пор, пока отправитель не отправит значение. Использование небуферизованных каналов в Go можно продемонстрировать на следующем примере:

func main() {
    c := make(chan int) // Unbuffered channel

    go func() {
        c <- 42 // Send value to channel
    }()

    fmt.Println(<-c) // Receive value from channel and print it
}

С другой стороны, буферизованные каналы позволяют хранить в канале несколько значений. При создании буферизованного канала емкость канала указывается с помощью второго аргумента make. Использование буферизованных каналов в Go можно продемонстрировать на следующем примере:

func main() {
    c := make(chan int, 1) // Buffered channel with capacity 1

    c <- 42 // Send value to channel

    fmt.Println(<-c) // Receive value from channel and print it
}

В примере мы создаем буферизованный канал с емкостью 1. Это означает, что канал может хранить одно значение без блокировки. Затем мы отправляем значение в канал с помощью оператора <- и получаем значение из канала с помощью того же оператора.

Каналы также можно использовать для синхронизации выполнения горутин. Используя каналы в качестве сигналов, мы можем заблокировать одну горутину до тех пор, пока другая горутина не отправит сигнал по каналу.

func main() {
    done := make(chan bool)

    go func() {
        // Do some work
        time.Sleep(1 * time.Second)
        done <- true // Send signal to channel
    }()

    <-done // Block until signal is received
}

В примере мы создаем канал done типа bool, который будет использоваться как сигнал. Затем мы создаем горутину, которая выполняет некоторую работу и по завершении отправляет сигнал на канал done. Затем основная горутина блокируется до тех пор, пока не будет получен сигнал.

III. Использование пакета sync: Координация выполнения нескольких горутин с помощью WaitGroups.

func main() {
    var wg sync.WaitGroup

    wg.Add(2)

    go func() {
        defer wg.Done()
        doSomething()
    }()

    go func() {
        defer wg.Done()
        doSomethingElse()
    }()

    // Wait for goroutines to finish executing
    wg.Wait()
}

func doSomething() {
    // Do something
}

func doSomethingElse() {
    // Do something else
}

sync.WaitGroup используется для координации выполнения нескольких горутин.

В Go sync.WaitGroup — это тип структуры, предоставляющий простой способ синхронизации выполнения нескольких горутин. Структура WaitGroup позволяет программе ждать, пока группа горутин завершит свою работу, прежде чем перейти к следующему шагу программы.

Структура WaitGroup содержит счетчик, который увеличивается каждый раз, когда создается новая горутина, и уменьшается, когда горутина завершает свою работу. Затем основная горутина может дождаться, когда счетчик достигнет нуля, используя метод Wait(), который блокируется до тех пор, пока все горутины не завершат свою работу.

Таким образом, Go предоставляет несколько способов добавления параллелизма в программы, включая горутины, каналы и пакет sync. Эти функции позволяют разработчикам писать параллельные и параллельные программы, которые являются более эффективными и масштабируемыми.