Vài điều hay ho về select trong golang
Go
50
golang
49
White

Hoàng Minh Trung viết ngày 12/04/2017

Linh tinh về select

Trong go thì cách dùng select cơ bản nhất sẽ để điều khiển read write vào các channel. Nắm rõ cách sử dụng select là vô cùng cần thiết cho intermediate gopher. Mô hình sử dụng select thì thông thường sẽ là:

select {
case <channel operation>:
    ....
case <channel operation>:
    ....
default:
    ....
}

Đọc từ channel

func CanWeRead(ch <-chan int) {
    select {
    case <-ch:
        .... // yes, we can read
    }
}

Tuy nhiên có 1 điểm cần chú ý ở đây là : nếu không có default thì xử lý sẽ bị block

func CanWeRead(ch <-chan int) {
    select {
    case <-ch:
        .... // yes, we can read
    default:
        .... // no, we could not read
    }
}

Đoạn code trên chính là mô tả dễ hiểu nhất cho việc hiểu thế nào là "non blocking IO"

Để sử dụng giá trị lấy ra từ channel thì có thể viết như dưới đây:

func CanWeRead(ch <-chan int) {
    select {
    case v, ok := <-ch:
        if !ok { // channel is closed 
            ...
        }
    }
}

Viết vào channel

func CanWeWrite(ch chan int) {
    select {
    case ch <- 1:
        ... // yes, we can write
    default:
        .... // no, we could not write
    }
}

Tương tự như read, default dành cho non-blocking writingwriting

Quiz time

Có 2 câu hỏi thú vị được đặt ra về việc đọc / ghi vào channel

Quiz 1: Đọc / ghi vào nil channel thì chuyện gì sẽ xảy ra?

var ch chan int
<- ch // ch chưa được initialize nên ch = nil
- 1. panic ?
- 2. block ?
- 3. other ?

Câu trả lời là 2, với nil channel thì cả read và write sẽ đều bị block. Việc này sẽ dẫn đến một cách dùng khá hay là việc thay đổi trạng thái block thông qua việc gán nil, ví dụ như trong trường hợp dưới đây thì chúng ta có thể điều khiển việc xử lý sẽ chui vào case 1 hay case 2 thông qua việc gán nil (hoặc ngược lại, gán giá trị)

select {
case <- ch1:
    ...
case <- ch2:
    ch1 = nil // disable previous case
}

Quiz 2: Đọc / ghi vào closed channel thì chuyện gì sẽ xảy ra?

time.AfterFunc(5 * time.Second, func() {
    close(done)
})
for {
    select {
    case <- done:
        return
    default:
        // 
    }
}
- 1. panic ?
- 2. block ?
- 3. other ?

Câu trả lời là 3: done channel sẽ bị block khi đọc bởi không có data, và khi close thì việc block sẽ được vô hiệu hoá và chúng ta có thể return xử lý ngay lập tức

Sử dụng reflect với select

Đôi khi cái bạn muốn select từ đó là không cố định, hoặc là bạn muốn select N chan cùng một lúc, khi đó thì syntax của go sẽ không support việc đó. Khi đó bạn phải hack bằng cách sử dụng reflection trong go. Điều đó được thể hiện trong ví dụ dưới đây:

package main

import (
    "fmt"
    "math/rand"
    "reflect"
    "sync"
    "time"
)

func multpleSelect(chans []chan bool) (int, bool, bool) {
    cases := make([]reflect.SelectCase, len(chans))
    for i, ch := range chans {
        cases[i] = reflect.SelectCase{
            Dir: reflect.SelectRecv,
            Chan: reflect.ValueOf(ch),
        }
    }

    i, v, ok := reflect.Select(cases)
    return i, v.Interface().(bool), ok
}

func main() {
    rand.Seed(time.Now().UnixNano())

    chans := make([]chan bool, 100)
    for i := 0; i < 100; i++ {
        chans[i] = make(chan bool)
    }


    var wg sync.WaitGroup
    go func() {
        wg.Add(1)

        if ch, v, ok := multpleSelect(chans); ok {
            fmt.Printf("I am chan-%v, value is %v\n", ch, v)
        }
        wg.Done()
    }()

    chans[rand.Int() % len(chans)] <- true

    wg.Wait()
}
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Hoàng Minh Trung

23 bài viết.
61 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
19 1
Bài viết dịch từ http://arslan.io/tenusefultechniquesingo Sử dụng một GOPATH duy nhất Sử dụng đồng thời nhiều GOPATH sẽ không giúp cho hệ thống ...
Hoàng Minh Trung viết 2 năm trước
19 1
White
18 15
(Ảnh) Mục đích của bài viết là hướng dẫn cơ bản nhất cho những ai chưa biết về docker, môi trường thực hiện là mac OS. Chuẩn bị Cài đặt virtua...
Hoàng Minh Trung viết gần 3 năm trước
18 15
White
14 0
Bài viết dịch từ https://github.com/luciotato/golangnotes/blob/master/OOP.md Mục đích bài viết Học golang dễ dàng hơn với những kiến thức bạn đ...
Hoàng Minh Trung viết 2 năm trước
14 0
Bài viết liên quan
White
9 2
Makefile thực hiện một số thao tác thường dùng trong Go Khi làm project Go mình thường tạo một file Makefile dạng này: Lưu ý nhớ thay thành tên m...
Huy Trần viết 2 năm trước
9 2
White
43 16
Go là gì? Dùng nó cho việc gì? Chắc hẳn đến thời điểm hiện tại, không ai là chưa nghe đến Go (hay còn gọi là Golang), một ngôn ngữ lập trình được ...
Huy Trần viết 3 năm trước
43 16
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
23 bài viết.
61 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!