Worker pool
Ví dụ: Bài toán cho bạn một con số n nhiệm vụ của chúng ta là xuất ra các con số tại vị trí từ 1 tới n trong dãy số Fibonacci đã cho.
package main
import (
"fmt"
)
func main() {
number := 10
for i := 1; i <= number ; i++{
fmt.Println(fib(i))
}
}
func fib(n int) int{
if n <= 1{
return n
}
return fib(n-1) + fib(n-2)
}
Kết quả:
1
1
2
3
5
8
13
21
34
55- Giả sử chúng ta có số n rất lơn để chương trình xử lý một cách nhanh hơn ta có thể phân luồng cho nó:
package main
import (
"fmt"
)
func main() {
number := 10
numberOfWorker := 5
jobs := make(chan int, number)
results := make(chan int, number)
for i := 0; i < numberOfWorker; i++{
go worker(jobs, results)
}
for i := 1; i <= number; i++{
jobs <- i
}
close(jobs)
for j := 0; j < number; j++{
fmt.Println(<-results)
}
}
func worker(jobs <-chan int, results chan<- int){
for n := range jobs{
results <- fib(n)
}
}
func fib(n int) int{
if n <= 1{
return n
}
return fib(n-1) + fib(n-2)
}
Kết quả
1
1
2
3
5
8
13
21
34
55- Tốc độ xử lý của cách thứ hai sẽ nhanh hơn mà chiếm nhiều bộ nhớ hơn cách 1
Creational pattern - Singleton
Singleton là một design pattern trong số 5 design pattern thuộc nhóm Creational Design Pattern bao gồm : Factory method, Abstract Factory, Builder, Prototype, Singleton.
Single Pattern đảm bảo rằng một class chỉ có duy nhất một instance, và cung cấp một cách toàn cầu để truy cấp tới instance đó.
Tại sao cần dùng Singleton Pattern
- Đa phần các đối tượng trong một ứng dụng đều chịu trách nhiệm cho công việc của chúng, truy xuất dữ liệu tự lưu trữ (self-contained data) và các tham chiếu trong phạm vi của chúng.
- Tuy nhiên, nhiều đối tượng có thêm những nhiệm vụ và có ảnh hưởng của nó rộng hơn. Chẳng hạn như quản lý các nguồn tài nguyên bị giới hạn hay là theo dõi toàn bộ trạng thái của hệ thống.
- Tuy nhiên, nhiều đối tượng có thêm những nhiệm vụ và có ảnh hưởng của nó rộng hơn. Chẳng hạn như quản lý các nguồn tài nguyên bị giới hạn hay là theo dõi toàn bộ trạng thái của hệ thống.
Singleton dùng để làm gì?
- Đảm bảo rằng 1 class chỉ có 1 instance duy nhất và class này luôn sẵn sàng để sử dụng ở bất kỳ thời điểm hoặc vị trí nào trong phần mềm ứng dụng của chúng ta.
- Việc quản lý việc truy cập tốt hơn vì chỉ có một thể hiện duy nhất.
- Có thể quản lý số lượng thể hiện của một lớp trong giới hạn chỉ định.
- Việc quản lý việc truy cập tốt hơn vì chỉ có một thể hiện duy nhất.
- Có thể quản lý số lượng thể hiện của một lớp trong giới hạn chỉ định.
Cài đặt Singleton Pattern trong golang
Trong Golang, có một số cách giúp chúng ta có thể cài đặt Singleton Pattern. Có thể kể tới như:
- Sử dụng func init()
- Sử dụng sync.Once
- Sử dụng hàm khởi tạo
- Sử dụng Mutex locks
- Sử dụng func init()
- Sử dụng sync.Once
- Sử dụng hàm khởi tạo
- Sử dụng Mutex locks
Khai báo : Tạo mới một dự án golang: Tạo foder src bên trong tao Foder singleton bên trong tạo file singleton.go:
package singleton
/*singleton interface*/
type Singleton interface{
AddOne() int
}
type singleton struct{
count int
}
var instance *singleton
/*GetInstance function return*/
func GetInstance() Singleton{
return &singleton{count: 100}
}
func (s *singleton) AddOne() int{
s.count++
return s.count
}
- Vào foder src bên trong tạo file main.go
package main
import (
"fmt"
"singleton/singleton"
)
func main() {
s1 := singleton.GetInstance()
s2 := singleton.GetInstance()
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", s2)
}
Vào thư mục src(C:\golang\src) vừa tạo ban đầu mở cmd lên chạy lệnh go mod init singleton
Tiếp tục chạy lệnh go run main.go và xem kết quả :
Cả s1 và s2 nó xuất về cùng một địa chỉ vùng nhớ.Bây giơ chúng ta sử lại file singleton.go
package singleton
/*singleton interface*/
type Singleton interface {
AddOne() int
}
type singleton struct {
count int
}
var instance *singleton
/*GetInstance function return*/
func GetInstance() Singleton {
if instance == nil {
instance = &singleton{count: 100}
}
return instance
}
func (s *singleton) AddOne() int {
s.count++
return s.count
}
- Vào main.go sửa lại
package main
import (
"fmt"
"singleton/singleton"
)
func main() {
s1 := singleton.GetInstance()
s2 := singleton.GetInstance()
fmt.Println(s1.AddOne())
fmt.Println(s1.AddOne())
fmt.Println(s2.AddOne())
fmt.Println(s2.AddOne())
fmt.Printf("%p\n", s1)
fmt.Printf("%p\n", s2)
}
Giải thích : Khi fmt.Println(s1.AddOne()) thì nó xuất ra 101 và fmt.Println(s1.AddOne()) chạy tiếp thì nó xuất ra 102 tương tự fmt.Println(s2.AddOne()) xuất ra 103 và fmt.Println(s2.AddOne()) xuất ra 104 Và hiện tại s1 và s2 nó trả về cùng một địa chỉ nhớ.
- Chạy lại chương trình ta thấy:
Đây là một singleton chúng ta đã cài đặt thành công, và chú ý cái singleton này chỉ đùng khi nó chạy tuần tự trên một tiến trình.
Để xem nếu chúng ta không chạy tuần tự sẻ như thế nào:
Bây giơ chúng ta sử lại file singleton.go
package singleton
import (
"time"
)
/*singleton interface*/
type Singleton interface {
AddOne() int
}
type singleton struct {
count int
}
var instance *singleton
/*GetInstance function return*/
func GetInstance() Singleton {
if instance == nil {
/*làm chậm chương trình để xem lỗi*/
time.Sleep(time.Second)
instance = &singleton{count: 100}
}
return instance
}
func (s *singleton) AddOne() int {
s.count++
return s.count
}
- Vào main.go sửa lại
package main
import (
"fmt"
"singleton/singleton"
"time"
)
func main() {
for i := 0; i < 10; i++ {
go func() {
fmt.Printf("%p\n", singleton.GetInstance())
}()
}
time.Sleep(time.Second * 10)
}
Và chúng ta sẻ thấy kết quả :
Ta xem bây giờ địa chỉ vùng nhớ của nó đã không còn giống nhau nữa lý do là vì khi có nhiều tiến trình cùng gọi vào hàm GetInstance() và cùng kiểm tra giá trị nill thi lúc đó chưa có khởi tạo xong thì các lần đó đều vượt qua kiểm tra if instance == nil và nhảy vào tạo các instance = &singleton{count: 100} vì thế nó tạo ra rất nhiều các instance dẫn đến tình trạng như trên.
- Để khắc phục tình trạng đó chúng ta cần sửa lại GetInstance() trong golang có hỗ trợ một thư viện để khắc phục tình trạng này :
Bây giơ chúng ta sử lại file singleton.go
package singleton
import (
"time"
"sync"
)
/*singleton interface*/
type Singleton interface {
AddOne() int
}
type singleton struct {
count int
}
var (
instance *singleton
once sync.Once
)
/*GetInstance function return*/
func GetInstance() Singleton {
once.Do(func() {
time.Sleep(time.Second)
instance = &singleton{count: 100}
})
return instance
}
func (s *singleton) AddOne() int {
s.count++
return s.count
}
- Vào main.go sửa lại
package main
import (
"fmt"
"singleton/singleton"
"time"
)
func main() {
for i := 0; i < 10; i++ {
go func() {
fmt.Printf("%p\n", singleton.GetInstance())
}()
}
time.Sleep(time.Second * 10)
}
Bây giờ chúng ta xem kết quả:
Mặc dù có hàm time.Sleep(time.Second) nhưng chương trình vẫn ra kết quả đúng. Hàm once.Do(func() đảm bào cho instance = &singleton{count: 100} nó chỉ thực hiện duy nhất đúng một lần.Cách 2: bây giờ chúng ta muốn khi bắt đầu chạy chương trình thi nó đã khởi tạo cho chúng ta một instance và khi nào dùng chỉ cần gọi hàm trả về một instance :
Bây giơ chúng ta sử lại file singleton.go
package singleton
import (
"sync"
)
/*singleton interface*/
type Singleton interface {
AddOne() int
}
type singleton struct {
count int
}
var (
instance *singleton
once sync.Once
)
func init() {
instance = &singleton{count: 100}
}
/*GetInstance function return*/
func GetInstance() Singleton {
return instance
}
func (s *singleton) AddOne() int {
s.count++
return s.count
}
- Vào main.go sửa lại
package main
import (
"fmt"
"singleton/singleton"
"time"
)
func main() {
for i := 0; i < 10; i++ {
go func() {
fmt.Printf("%p\n", singleton.GetInstance())
}()
}
time.Sleep(time.Second * 10)
}
Kết quả
Sự khác nhau ở hai cách này là ở cách 2 khi bắt đầu chạy chương trình thi nó đã khởi tạo cho chúng ta một instance và khi nào dùng chỉ cần gọi hàm trả về một instance còn ở cách 1 thì khi nào gọi đến hàm GetInstance() nó mới bất đầu kiểm tra rồi khỏi tạo instance cho chúng ta.
0 Nhận xét