Vài điều hay ho về select trong golang
Go
39
golang
38
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.
65 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
22 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 hơn 2 năm trước
22 1
White
19 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 hơn 3 năm trước
19 15
White
17 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 hơn 2 năm trước
17 0
Bài viết liên quan
White
10 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 hơn 2 năm trước
10 2
White
16 0
Crawl dữ liệu Crawl là một vấn đề hay gặp trong quá trình làm software. Ví dụ lấy tin tức, tin giảm giá, vé xem phim... là những dạng của crawl. Mộ...
Thach Le viết hơn 2 năm trước
16 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

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


White
{{userFollowed ? 'Following' : 'Follow'}}
23 bài viết.
65 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á!