Привет всем, меня зовут Мин.
Во время самостоятельного изучения Golang я написал подробные учебные заметки и разместил их в своем личном общедоступном аккаунте WeChat «Время программирования Go». вы только изучаете язык Go, уделяйте ему внимание, учитесь и развивайтесь вместе.
Мой онлайн-блог:golang.iswbm.comМой Github: github.com/iswbm/GolangCodingTime
Я написал двумя разделами ранее наswitch-case
статьи, а именно:
Управление потоком: переключатель-кейс
Усвоить урок сегодня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 аналогичен переключателю, но его сценарии использования более специфичны.После изучения этой статьи вам необходимо знать следующие отличия:
- select можно использовать только для операций с каналом (запись/чтение), тогда как switch более общий;
- Случай выбора является случайным, а случай переключения выполняется последовательно;
- Select должен обратить внимание на то, чтобы избежать взаимоблокировок, а также может сам реализовать механизм тайм-аута;
- Нет использования fallthrough, аналогичного switch в select;
- select не может принимать функции или другие выражения, такие как switch.