Go – Select

Trong ngôn ngữ Go, câu lệnh select giống như câu lệnh Switch, nhưng trong câu lệnh select, câu lệnh case liên quan đến việc trao đổi thông tin ví dụ như các hoạt động nhận và gửi trên channel. Thông thường mỗi câu lệnh case sẽ được đọc thử từ channel. Khi một case bất kỳ sẵn sàng (channel được đọc), thì câu lệnh kết hợp với case đó được chạy. Nếu nhiều case sẵn sàng, nó sẽ lựa chọn ngẫu nhiên một. Bạn có thể có case mặc định được chạy nếu không có case nào sẵn sàng.

Cú pháp

select {

  case SendOrReceive1: // Câu lệnh
  case SendOrReceive2: // Câu lệnh
  case SendOrReceive3: // Câu lệnh
  .......
  default: // Câu lệnh

Hãy cùng xem ví dụ sau đây

package main
import "fmt"
import "time"

// Đẩy dữ liệu cho channel và chờ 4 giây
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

// Đẩy dữ liệu cho channel và chờ 2 giây
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    // Tạo các biến channel để truyền giá trị string
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    // Gọi các goroutine cùng với các biến channel
    go data1(chan1)
    go data2(chan2)
    
    //Cả hai câu lệnh case đều đợi dữ liệu trong chan1 hoặc chan2
    //chan2 nhân dữ liệu trước do nó chỉ chờ 2 giây trong data2().
    //Nên case thứ 2 sẽ chạy và thoát khỏi khối select
    select {
      case x := <-chan1:
        fmt.Println(x)
      case y := <-chan2:
        fmt.Println(y)
    }
}

Ở đây câu lệnh select đợi dữ liệu sẵn sàng trong channel bất kỳ. data2() thêm dữ liệu vào channel sau khi tạm dừng 2 giây do đó câu lệnh case thứ 2 được chạy.

Kết quả

from data2()

Case mặc định

case mặc định trong câu lệnh select được chạy khi không có case nào sẵn sàng. Nó thường được sử dụng để ngăn việc câu lệnh select chặn chương trình chạy.

Hãy cùng thêm case default (mặc định) cho chương trình bên trên và cùng xem kết quả chạy. Ở đây khi chạy đến đoạn select, nếu không có channel nào có dữ liệu ở trạng thái sẵn sàng, nó sẽ chạy đoạn code mặc định mà sẽ không đợi cho đến khi một kênh bất kỳ có dữ liệu

package main
import "fmt"
import "time"

// Đẩy dữ liệu cho channel và chờ 4 giây
func data1(ch chan string) {  
    time.Sleep(4 * time.Second)
    ch <- "from data1()"
}

// Đẩy dữ liệu cho channel và chờ 2 giây
func data2(ch chan string) {  
    time.Sleep(2 * time.Second)
    ch <- "from data2()"
}

func main() {
    // Tạo các biến channel để truyền giá trị string  
    chan1 := make(chan string)
    chan2 := make(chan string)
    
    // Gọi các goroutine cùng với các biến channel
    go data1(chan1)
    go data2(chan2)

    // Cả hai câu lệnh case kiểm tra dữ liệu trong chan1 or chan2.
    // Nhưng dữ liệu không sẵn sàng (Cả 2 routines đều tạm dừng 2 giây và 4 giây)
    // Nên đoạn code trong default sẽ được chạy mà không chờ dữ liệu trong các channel.
    select {
    case x := <-chan1:
        fmt.Println(x)
    case y := <-chan2:
        fmt.Println(y)
    default:
    	fmt.Println("Default case executed")
    }
}

Kết quả

Default case executed

Đợi vô hạn

Nếu câu lệnh select không có bất kỳ câu lệnh case nào thì câu lệnh select sẽ đợi vô hạn và kết quả sẽ dẫn đến một deadlock trong chương trình

Cú pháp như sau

select{}

Hãy cùng xem ví dụ sau

package main 
  
// Hàm main 
func main() {    
   // Câu lệnh Select  
   // không có case 
   select{ } 
}

Kết quả

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:
main.main()
    /home/runner/main.go:9 +0x20
exit status 2

Deadlock

Câu lệnh select bị chặn khi không có một câu lệnh case nào ở trạng thái sẵn sàng và câu lệnh select không chứa bất kỳ case mặc định nào, thì câu lệnh select sẽ bị chặn cho tới khi ít nhất có một case hoặc việc trao đổi thông tin được tiến hành

Hãy cùng xem ví dụ sau

package main 
  
// Hàm main 
func main() { 
      
    // tạo channel 
    mychannel:= make(chan int) 
      
    // channel không sẵn sàng 
   // và không có case mặc định
   select{ 
       case <- mychannel: 
   } 
}

Trong chương trình trên chúng ta tạo một channel và thử đọc từ channel này trong select. Câu lệnh select sẽ chờ mãi mãi bởi vì không có Goroutine nào ghi vào channel và do đó kết quả là deadlock giống với trường hợp bên trên

Kết quả

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /home/runner/main.go:14 +0x52
exit status 2

Nhiều case cùng ở trạng thái sẵn sàng

Trong câu lệnh select, nếu nhiều case cùng ở trạng thái sẵn sàng để xử lý, thì một trong những case đó có thể được lựa chọn ngẫu nhiên

Hãy cùng xem ví dụ sau đây

package main 
  
import "fmt"
      
      
    // Hàm 1 
    func portal1(channel1 chan string){ 
        for i := 0; i <= 3; i++{ 
            channel1 <- "Welcome to channel 1"
        } 
          
    } 
      
    // Hàm 2 
     func portal2(channel2 chan string){ 
        channel2 <- "Welcome to channel 2"
    } 
  
// Hàm main 
func main() { 
      
    // Tạo các channel
   R1:= make(chan string) 
   R2:= make(chan string) 
     
   // Gọi hàm 1 và  
   // hàm 2 trong goroutine 
   go portal1(R1) 
   go portal2(R2) 
     
   // lựa chọn select  
   // của trường hợp này là ngẫu nhiên 
   select{ 
       case op1:= <- R1: 
       fmt.Println(op1) 
       case op2:= <- R2: 
       fmt.Println(op2) 
   } 
}

Kết quả

Welcome to channel 2

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.