tpphu / golang-training

Golang for Backend Developer with Nordic Coder
https://nordiccoder.com/khoa-hoc/golang-for-backend-dev/
131 stars 50 forks source link

Trả lời câu hỏi, vì sao trong ví dụ gorountine-pipline channel có thể push vào vượt qua cả capacity của nó. #11

Closed tpphu closed 5 years ago

tpphu commented 5 years ago

Trả lời câu hỏi, vì sao trong ví dụ gorountine-pipline channel có thể push vào vượt qua cả capacity của nó.

Ví dụ này thấy trên lớp nó không chạy đúng như dự định ban đầu, channel ngay lập tức có thể đẩy vào được 2 giá trị.

tpphu commented 5 years ago

Phú đã kiểm chứng và thấy cách chúng ta hiểu về channel cũng capacity/buffer của nó vẫn còn đúng. Tuy nhiên do cách chúng ta in ra màn hình dòng Println trước hành động push vào channel nên nó như vậy thôi.

Phú sẽ giải thích trong buổi học buổi tối cho các bạn.

tpphu commented 5 years ago

Các bạn chạy câu lệnh để limit CPU để thấy thêm chi tiết.

Với 1 CPU

$ GOMAXPROCS=1 go run main.go

Bạn sẽ thấy output có thứ tự và dễ hiểu như sau:

[GEN] channel: 2
[GEN] channel: 3
[GEN] channel: 4
[GEN] channel: 5
[SQ] channel: 4
[OUT]: 4
[SQ] channel: 9
[OUT]: 9
[SQ] channel: 16
[OUT]: 16
[SQ] channel: 25
[OUT]: 25

Với 4 CPU

Bạn sẽ thấy các giá trị in ra hơi lộn xộn, tuy nhiên nó vẫn đúng, và bạn cần phải suy nghĩ tại sao nó như vậy

$ GOMAXPROCS=4 go run main.go
[SQ] channel: 4
[GEN] channel: 2
[GEN] channel: 3
[GEN] channel: 4
[GEN] channel: 5
[OUT]: 4
[SQ] channel: 9
[OUT]: 9
[SQ] channel: 16
[OUT]: 16
[SQ] channel: 25
[OUT]: 25
ghost commented 5 years ago

ubuntu@ubuntu:~/go/src/golang-trainning/gorountine-pipline$ GOMAXPROCS=1 go run main.go GEN 2 GEN 3 SQ 4 GEN 4 OUT 4 SQ 9 GEN 5 OUT 9 SQ 16 OUT 16 SQ 25 OUT 25 ubuntu@ubuntu:~/go/src/golang-trainning/gorountine-pipline$ GOMAXPROCS=4 go run main.go GEN 2 GEN 3 SQ 4 GEN 4 OUT 4 SQ 9 GEN 5 OUT 9 SQ 16 OUT 16 SQ 25 OUT 25

em thấy nó cũng vậy. Máy của em là VMware ubuntu 4 core.

Em có một giả thuyết thế này: không bao quan trọng là bao nhiêu cpu. mà quan trọng là cơ chế khóa channel phải đảm bảo lúc nào trong channel cũng có một đơn vị. Còn vấn đề nó xuất cái gì trước sau đó là do các thread hoàn thành nhanh hay chậm

tpphu commented 5 years ago

Anh sẽ kiểm tra lại, trên ubuntu, nhưng về lý thuyết chạy 1CPU phải khác với 4CPU không thể cùng kết quả được.

Mình sẽ có cần chữ này đằng trước Ubuntu:

$ env GOMAXPROCS=4 go run main.go
$ env GOMAXPROCS=1 go run main.go

Em thử lại xem nha.

tpphu commented 5 years ago

Nếu chạy 1CPU và 4CPU mà bằng nhau, thì những gì chúng ta nghĩ về ngôn ngữ lập trình chỗ này sẽ có gì đó không đúng.

tpphu commented 5 years ago

Đây là một ví dụ chạy thực tế:

https://play.golang.org/p/ZSBitGdqcSo

Do vậy em có thể copy và chạy thử cái này:

package main

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

// https://gobyexample.com/channel-buffering
// Nhan vao mot danh sach cac so nguyen
// Tra ra mot channel
func gen(nums ...int) <-chan int {
    // Run Step 1
    // Bufferred Channel
    out := make(chan int, 4) //capacity
    // Run Step 2
    go func() {
        for _, n := range nums {
            // Chi day dc vao channel khi ma len < capacity
            out <- n
            // In ra o day co nghia la da push dc
            fmt.Printf("\n[GEN] channel: %d", n)
        }
        close(out)
    }()
    // Run Step
    return out
}

func sq(in <-chan int) <-chan int {
    // Unbufferred Channel
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
            fmt.Printf("\n[SQ] channel: %d", n*n)
        }
        close(out)
    }()
    return out
}

func main() {
    fmt.Println("Num CPUs:", runtime.NumCPU())
    runtime.GOMAXPROCS(1)
    // Set up the pipeline.
    c := gen(2, 3, 4, 5)
    out := sq(c)

    // Consume the output.
    go func() {
        // Vi out la Unbufferred Channel
        // nen khi sleep, thi func sq khong the push them dc value vao sq channel
        // do vay ban se khong thay dong in ra
        for v := range out {
            time.Sleep(1 * time.Second)
            fmt.Printf("\n[OUT]: %d", v)
        }
    }()
    time.Sleep(30 * time.Second)
}