+1

[Golang] Channel trong golang và use case - part III

Mở đầu

  • Tiếp tục series, hôm nay là một buổi chia sẽ của tôi về use case của channel trong GO. Let's go, guys!

Use case 2: Implement semaphore pattern trong golang

  • Như mọi lần chúng ta sẽ đi làm vài vòng qua định nghĩa. Theo wikipedia, ta sẽ có định nghĩa hoàn chỉnh về semaphore như sau: semaphore is a variable or abstract data type used to control access to a common resource by multiple threads and avoid critical section problems in a concurrent system such as a multitasking operating system.
  • Trong một currency system, resource sẽ bị truy cập đồng thời bởi nhiều process. Và semarphore được xem như là một cơ chế để quản lý các truy cập các resource đó.
  • Những ứng dụng của semaphore mà chúng ta có thể thấy là:
    • Limit database connection pool
    • Networking connection throttling
    • Limiting concurrent access to resources such as a disk
  • Sau đây là một simple implement của semaphore trong golang dựa vào đặc tính của channel
    images.jpeg
    • Giả lập chúng ta có N request cần được xử lý một cách đồng thời. Mỗi request chúng ta cần số M thời gian nhất định để xử lí.
      func main(){
          numberOfRequest := 10 
          for i := 0; i < numberOfRequest; i++ {
              go handleRequest(strconv.Itoa(i))
          }
      
          /* sleep to see what happen */
          time.Sleep(time.Second * 12)
      }
      
      func handleRequest(requestName string) {
          fmt.Printf("%v: request %v: is accessing resource \n", time.Now().Format("15:04:05"), requestName)
          time.Sleep(time.Second * 3)
      }
      result:
          16:14:06: request 4: is accessing resource
          16:14:06: request 9: is accessing resource
          16:14:06: request 5: is accessing resource
          16:14:06: request 6: is accessing resource
          16:14:06: request 7: is accessing resource
          16:14:06: request 8: is accessing resource
          16:14:06: request 1: is accessing resource
          16:14:06: request 3: is accessing resource
          16:14:06: request 0: is accessing resource
          16:14:06: request 2: is accessing resource
      
    • Chúng ta có thể thấy cùng một lúc tai giây 16:14:05 có đang có 10 request đang truy cập resource. Tiếp theo, dựa vào đặc tính block goroutine hiện tai của buffer chanel khi sức chứa của nó bị quá tải, chúng ta sẽ chỉ cho phép tối đa là 3 truy cập cùng lúc trên resource.
       func main(){
           maxConcurrency := 3
           sem := make(chan struct{}, maxConcurrency)
      
           numberOfRequest := 10 
           for i := 0; i < numberOfRequest; i++ {
      
               sem <- struct{}{} // acquire the resource
      
               go handleRequest(strconv.Itoa(i))
           }
      
           /* sleep to see what happen */
           time.Sleep(time.Second * 12)
       }
      
       func handleRequest(requestName string) {
           fmt.Printf("%v: request %v: is accessing resource \n", time.Now().Format("15:04:05"), requestName)
           time.Sleep(time.Second * 3)
       }
       
       result:
           16:30:30: request 1: is accessing resource
           16:30:30: request 0: is accessing resource
           16:30:30: request 2: is accessing resource
           fatal error: all goroutines are asleep - deadlock!
      
           goroutine 1 [chan send]:
           main.main()
                   D:/projects/semaphores/main.go:15 +0x59
           exit status 2
      
      
    • Deadlock xuất hiện bởi vì không có goroutine nào đến giải phóng space cho channel. Giống như ở siêu thị, một hàng dài khách hàng đang đợi tính tiền và cashier machine bổng dưng dở chứng, dẩn đến một tình trạng ứ đọng. Để giải quyết, chúng ta phải làm thêm 1 nhiệm vụ là: release resource khi đã xử dụng xong.
      ...
      func handleRequest(requestName string, sem chan struct{}) {
          defer release(sem)
      
          fmt.Printf("%v: request %v: is accessing resource \n", time.Now().Format("15:04:05"), requestName)
          time.Sleep(time.Second * 3)
      }
      
      func release(sem chan struct{}) {
          <-sem
      }
      
    • Running và see what happen!
        16:45:24: request 0: is accessing resource
        16:45:24: request 1: is accessing resource
        16:45:24: request 2: is accessing resource
        16:45:27: request 3: is accessing resource
        16:45:27: request 4: is accessing resource
        16:45:27: request 5: is accessing resource
        16:45:30: request 6: is accessing resource
        16:45:30: request 7: is accessing resource
        16:45:30: request 8: is accessing resource
        16:45:33: request 9: is accessing resource
      
      Request lúc này đã được seperate thành từng nhóm gồm 3 request và truy cập vào giây 24, 27, 30 và 33 giống như nguyên lý của sermaphore.

Tạm kết

  • Dựa vào phần trình bày ở trên, hi vọng có thể giúp bạn hiểu thêm về semarphore pattern. Bạn hoàn toàn có thể tùy biến nó để giải quyết một số bài toán tương tự như shushi bar, sleep barber hay bài toán craw url with only 5 workers mà mình đã giới thiệu ở part II .
  • Ngoài ra, golang cũng có một semarphore package chính thức tại đây github .
  • Thanks for reading! See you on next part!

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí