29. Выберите использование на языке Go

Go

Привет всем, меня зовут Мин.

Во время самостоятельного изучения Golang я написал подробные учебные заметки и разместил их в своем личном общедоступном аккаунте WeChat «Время программирования Go». вы только изучаете язык Go, уделяйте ему внимание, учитесь и развивайтесь вместе.

Мой онлайн-блог:golang.iswbm.comМой Github: github.com/iswbm/GolangCodingTime


Я написал двумя разделами ранее наswitch-caseстатьи, а именно:

Управление потоком: переключатель-кейс

Утверждение типа в Go

Усвоить урок сегодняswitch-caseАналогично, но немного больше个人特色изselect-case, этот раздел должен был быть помещен вИзучаем го-процедуры: подробное описание каналов/каналовЯ говорил об этом вместе, но я пропустил это в то время, я не заметил этого, пока мне не подсказал читатель, поэтому я буду использовать эту статью, чтобы дополнить ее сегодня.

По сравнению с switch-case использование select-case относительно простое, и его можно использовать только для операций, связанных с каналом/каналом.

select {
    case 表达式1:
        <code>
    case 表达式2:
        <code>
  default:
      <code>
}

Далее давайте рассмотрим несколько примеров, которые помогут понять эту модель выбора.

1. Самый простой пример

Сначала создайте два канала и отправьте данные на c2 в select

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan string, 1)
    c2 := make(chan string, 1)

    c2 <- "hello"

    select {
    case msg1 := <-c1:
      fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
      fmt.Println("c2 received: ", msg2)
    default:
      fmt.Println("No data received.")
    }
}

При запуске select он будет проходить все (если есть шанс) case-выражения, пока есть канал, который получил данные, тогда select завершится, поэтому вывод будет следующим

c2 received:  hello

2. Избегайте взаимоблокировок

Во время выполнения select должна быть затронута одна из ветвей.

Если после обхода всех кейсов попаданий нет (命中: Возможно, это описание не точно, я имел в виду, что оператор операции канала может быть выполнен) Любое выражение case войдет в ветку кода по умолчанию.

Но если вы не напишете ветку по умолчанию, select будет блокироваться до тех пор, пока не появится случай, который может быть сбит, и если совпадения нет, select выкинетdeadlockошибка, как показано ниже.

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan string, 1)
    c2 := make(chan string, 1)

    // c2 <- "hello"

    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
        // default:
        //     fmt.Println("No data received.")
    }
}

Вывод после запуска следующий

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select]:
main.main()
        /Users/MING/GolandProjects/golang-test/main.go:13 +0x10f
exit status 2

Есть два способа решить эту проблему

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

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan string, 1)
    c2 := make(chan string, 1)

  // c2 <- "hello"

    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
    default:

    }
}

Другой - разрешить одному из каналов получать данные

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string, 1)
    c2 := make(chan string, 1)

  // 开启一个协程,可以发送数据到信道
    go func() {
        time.Sleep(time.Second * 1)
        c2 <- "hello"
    }()

    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
    }
}

3. выбрать случайность

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

Это видно из результата выполнения следующего примера

4. Тайм-аут выбора

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

package main

import (
    "fmt"
    "time"
)

func makeTimeout(ch chan bool, t int) {
    time.Sleep(time.Second * time.Duration(t))
    ch <- true
}

func main() {
    c1 := make(chan string, 1)
    c2 := make(chan string, 1)
    timeout := make(chan bool, 1)

    go makeTimeout(timeout, 2)

    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
    case <-timeout:
        fmt.Println("Timeout, exit.")
    }
}

Вывод выглядит следующим образом

Timeout, exit.

5. Доступны как чтение/запись

Случай в приведенном выше примере, кажется, только читает данные из канала, но на самом деле выражение case в select требует, чтобы вы работали только с каналом, независимо от того, записываете ли вы данные в канал или читаете данные из канала.

package main

import (
    "fmt"
)

func main() {
    c1 := make(chan int, 2)

    c1 <- 2
    select {
    case c1 <- 4:
        fmt.Println("c1 received: ", <-c1)
        fmt.Println("c1 received: ", <-c1)
    default:
        fmt.Println("channel blocking")
    }
}

Вывод выглядит следующим образом

c1 received:  2
c1 received:  4

6. Подведите итоги

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

  1. select можно использовать только для операций с каналом (запись/чтение), тогда как switch более общий;
  2. Случай выбора является случайным, а случай переключения выполняется последовательно;
  3. Select должен обратить внимание на то, чтобы избежать взаимоблокировок, а также может сам реализовать механизм тайм-аута;
  4. Нет использования fallthrough, аналогичного switch в select;
  5. select не может принимать функции или другие выражения, такие как switch.