Функция runtime.Gosched

Функция runtime.Gosched передает управление другим горутинам, приостанавливая текущую горутину и позволяя планировщику выполнить другие готовые к выполнению горутины. После того как другие горутины завершат свою работу или также передадут управление, выполнение текущей горутины будет продолжено. Эта функция полезна в ситуациях, когда необходимо дать возможность выполниться другим горутинам, особенно в бесконечных циклах.

Синтаксис

goruntime.Gosched()

Пример 1

Давайте рассмотрим базовый пример использования runtime.Gosched для передачи управления между горутинами:

gopackage main

import (
  "fmt"
  "runtime"
)

func main() {
  go func() {
    for i := 0; i < 5; i++ {
      fmt.Println("Goroutine 1:", i)
      runtime.Gosched()
    }
  }()

  go func() {
    for i := 0; i < 5; i++ {
      fmt.Println("Goroutine 2:", i)
      runtime.Gosched()
    }
  }()

  fmt.Scanln()
}

Результат выполнения кода может выглядеть так (порядок может меняться):

go"Goroutine 1: 0"
"Goroutine 2: 0"
"Goroutine 1: 1"
"Goroutine 2: 1"
"Goroutine 1: 2"
"Goroutine 2: 2"
"Goroutine 1: 3"
"Goroutine 2: 3"
"Goroutine 1: 4"
"Goroutine 2: 4"

Пример 2

Использование runtime.Gosched в бесконечном цикле для предотвращения блокировки других горутин:

gopackage main

import (
  "fmt"
  "runtime"
  "time"
)

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

  go func() {
    for {
      select {
      case <-done:
        fmt.Println("Worker stopped")
        return
      default:
        fmt.Println("Working...")
        runtime.Gosched()
        time.Sleep(100 * time.Millisecond)
      }
    }
  }()

  time.Sleep(500 * time.Millisecond)
  done <- true
  time.Sleep(100 * time.Millisecond)
}

Результат выполнения кода:

go"Working..."
"Working..."
"Working..."
"Working..."
"Worker stopped"

Пример 3

Сравнение работы с runtime.Gosched и без нее в интенсивных вычислениях:

gopackage main

import (
  "fmt"
  "runtime"
  "time"
)

func heavyTask(id int) {
  sum := 0
  for i := 0; i < 100000000; i++ {
    sum += i
    if i%1000000 == 0 {
      runtime.Gosched()
    }
  }
  fmt.Printf("Task %d completed, sum: %d\n", id, sum)
}

func main() {
  start := time.Now()

  for i := 0; i < 3; i++ {
    go heavyTask(i)
  }

  time.Sleep(1 * time.Second)
  fmt.Println("Main goroutine continues...")
  time.Sleep(1 * time.Second)
  fmt.Println("Main goroutine finished")
}

Результат выполнения кода:

go"Main goroutine continues..."
"Task 0 completed, sum: 4999999950000000"
"Task 1 completed, sum: 4999999950000000"
"Task 2 completed, sum: 4999999950000000"
"Main goroutine finished"

Пример 4

Пример с тремя горутинами, где каждая передает управление после выполнения части работы:

gopackage main

import (
  "fmt"
  "runtime"
)

func process(id int, steps int) {
  for i := 1; i <= steps; i++ {
    fmt.Printf("Goroutine %d: step %d\n", id, i)
    if i%2 == 0 {
      runtime.Gosched()
    }
  }
}

func main() {
  go process(1, 5)
  go process(2, 5)
  go process(3, 5)

  fmt.Scanln()
}

Результат выполнения кода (порядок может меняться):

go"Goroutine 1: step 1"
"Goroutine 2: step 1"
"Goroutine 3: step 1"
"Goroutine 1: step 2"
"Goroutine 2: step 2"
"Goroutine 3: step 2"
"Goroutine 1: step 3"
"Goroutine 2: step 3"
"Goroutine 3: step 3"
"Goroutine 1: step 4"
"Goroutine 2: step 4"
"Goroutine 3: step 4"
"Goroutine 1: step 5"
"Goroutine 2: step 5"
"Goroutine 3: step 5"

Смотрите также

  • функцию runtime.GOMAXPROCS,
    которая устанавливает количество одновременно выполняющихся потоков
  • функцию runtime.NumGoroutine,
    которая возвращает количество активных горутин
  • функцию runtime.Goexit,
    которая завершает выполнение текущей горутины
  • функцию runtime.NumCPU,
    которая возвращает количество доступных процессоров