Bài 08: Golang - Nhập Môn Cơ Bản

Bài 08: Golang - Nhập Môn Cơ Bản

  

Channel

1: Khái niệm

Channels có thể được coi là các đường ống sử dụng mà Goroutines giao tiếp. Tương tự như cách nước chảy từ đầu này sang đầu kia trong đường ống, dữ liệu có thể được gửi từ một đầu và nhận từ đầu kia bằng channels.

2:Khai báo

package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg = sync.WaitGroup{}
    ch := make(chan int)
    wg.Add(2)
    go func(){
        i := <- ch
        fmt.Println(i)
        wg.Done()
    }()
    go func(){
        ch <- 50
        wg.Done()
    }()
    wg.Wait()      
}
-    Giải thích : cách thức hoạt động của channel là : khi hai go func()  được start để chạy thi nó sẻ tạo ra hai luồng để chạy song song. khi chương trình chạy đến i := <- ch  nó gán cho i một channel và nó sẻ đứng chờ đên khi nào channel có giá trị được đưa vào thì mới chạy tiếp không thi nó sẽ chờ tiếp.khi    go func()  thứ hai chạy thì channel được gán cho giá trịvà đẩy vô channel là 50 và khi channel có giá trị nó đẩy lên cho go func() thứ nhất và gán giá trị cho i và chúng ta mới xuất i ra và kết quả là 50.
Tương tự ta có ví dụ thứ 2:
package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg = sync.WaitGroup{}
    ch := make(chan int)
    wg.Add(2)
    go func(){
        i := <- ch
        fmt.Println(i)
        ch <- 10
        wg.Done()
    }()
    go func(){
        ch <- 50
        fmt.Println(<- ch)
        wg.Done()
    }()
    wg.Wait()      
}
kết quả :
50
10
Giải thích:  go func()  thứ hai sẻ gửi cho  go func()  thứ nhất giá trị channel là 50 và  go func()  thứ nhất sẻ gửi cho  go func()  thứ hai giá trị channel là 10 vì vậy khi chúng ta xuất ra giá trị của  go func()  thứ nhất là 50 và  go func()  thứ hai sẽ là 10.
-    Tuy nhiên cách này không thông dụng cho việc gửi và nhận giữa các Goroutines nó làm cho chương trình golang của chúng ta trở nên rối hơn, Ví vậy chúng ta sẽ tạo ra các Goroutines chuyên nhận dữ liệu và các Goroutines chuyên gửi dữ liệu.

package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg = sync.WaitGroup{}
    ch := make(chan int)
    wg.Add(2)
    go func(ch <-chan int){
        i := <- ch
        fmt.Println(i)
        wg.Done()
    }(ch)
    go func(ch chan<- int){
        ch <- 50
        wg.Done()
    }(ch)
    wg.Wait()      
}
Kết quả : in ra 50
Trường hợp khác nếu chúng ta gửi hai channel và chỉ có nhận một thi sẻ ra sao:
package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg = sync.WaitGroup{}
    ch := make(chan int)
    wg.Add(2)
    go func(ch <-chan int){
        i := <- ch
        fmt.Println(i)
        wg.Done()
    }(ch)
    go func(ch chan<- int){
        ch <- 50
        ch <- 50
        wg.Done()
    }(ch)
    wg.Wait()      
}
Kết quả : chương trình báo lỗi
Để khắc phục lỗi này chúng ta cấp phát thêm cho nó một không gian lưu trữ phụ:
package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg = sync.WaitGroup{}
    ch := make(chan int, 50)
    wg.Add(2)
    go func(ch <-chan int){
        i := <- ch
        fmt.Println(i)
        wg.Done()
    }(ch)
    go func(ch chan<- int){
        ch <- 50
        ch <- 50
        wg.Done()
    }(ch)
    wg.Wait()      
}
Kết quả chương trình chạy in ra : 50
tương tự có thể thay  ch <- 50  thứ hai với một số khác.
Để tránh sinh ra  lỗi một channel gửi quá nhiều và một channel nhận quá ít hay ngược lại thì ta có thể sử dụng như sau:
package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg = sync.WaitGroup{}
    ch := make(chan int, 50)
    wg.Add(2)
    go func(ch <-chan int){
        for i := range ch {
            fmt.Println(i)
        }
        wg.Done()
    }(ch)
    go func(ch chan<- int){
        ch <- 10
        ch <- 20
        ch <- 50
        ch <- 100
        close(ch)
        wg.Done()
    }(ch)
    wg.Wait()      
}
Sau khi gửi xong ta sử dụng cloes(ch) để gửi tính hiêu là đã hoàn tất quá trình gửi cho hàm nhận biết để hàm nhận không phải chờ nhận hết kích thước được khai báo. 

Pipeline fan-in fan-out

Ví dụ : bài toán cho chúng ta biết một mảng các con số nguyên từ 1 tới 10 chúng ta có nhiệm vụ là viết chương trình trả về tổng bình phương trong cái mảng ban đầu đó.
package main
import (
    "fmt"
)
func main() {
    randomNumbers := []int{}
    for i := 1; i <= 10; i++{
        randomNumbers = append(randomNumbers,i)
    }
    sum := 0
    for i := 1; i < len(randomNumbers); i++{
        sum += randomNumbers[i] * randomNumbers[i]
    }
    fmt.Printf("Tota sum of Square: %d ", sum)
}
Kết quả :    Tota sum of Square: 384 
Giả sử chúng ta có một mảng các số nguyên rất lớn để tối ưu chương trình chúng ta thực hiện sử dụng Pipeline fan-in fan-out để chia nhỏ ra thành nhiều luống chạy song song:
package main
import (
    "fmt"
)
func main() {
    randomNumbers := []int{}
    for i := 1; i <= 100000; i++{
        randomNumbers = append(randomNumbers,i)
    }
    inputChan := generatePipeline(randomNumbers)
    //Fan-out
    c1 := fanOut(inputChan)
    c2 := fanOut(inputChan)
    c3 := fanOut(inputChan)
    c4 := fanOut(inputChan)
    //Fan-in
    c := fanIn(c1, c2, c3, c4)
       
    sum := 0
    for i := 0; i < len(randomNumbers); i++{
        sum += <-c
    }
    fmt.Printf("Tota sum of Square: %d ", sum)
}
func generatePipeline(numbers []int) <-chan int{
    out := make(chan int)
    go func(){
        for _, n := range numbers{
            out <- n
        }
        close(out)
    }()
    return out
}
func fanOut(in <-chan int) <-chan int{
    out := make(chan int)
    go func(){
        for n := range in{
            out <- n * n
        }
        close(out)
    }()
    return out
}
func fanIn(inputChannel ...<-chan int) <-chan int{
    in := make(chan int)
    go func(){
        for _, c := range inputChannel{
            for n := range c{
                in <- n
            }
        }
    }()
    return in
}
  Kết quả :    Tota sum of Square: 333338333350000 

Đăng nhận xét

0 Nhận xét

myadcash