Hướng dẫn dùng golang time.sleep trong PHP
Một trong các điểm mạnh của Go đó là khả năng sử lý đa luồng nó là một trong những vấn đề được các nhà phát triển golang chú trọng hàng đầu. Go đưa ra 2 tính năng hỗ trợ concurrency rất mạnh đó là Goroutine và Channel. Ở bài viết này chúng ta sẽ tập trung vào Goroutine trong Go. Trước tiên cùng đi qua các khái niệm 1 chút.! 1. ConcurrencyXử lý concurrency (đồng thời) nghĩa là có khả năng giải quyết nhiều công việc một lúc, và những công việc đó ko nhất thiết phải xảy ra tại cùng một thời điểm. Hay nói ngắn gọn hơn là việc nhiều task được xử lý cùng một lúc. 2. GoroutinesCơ chế hoạt động và cách sử dụngCơ chế của goroutine khá đơn giản: 1 function tồn tại một cách đa luồng với các goroutine khác trên cùng một không gian bộ nhớ, Go có bộ điều khiển quản lý các goroutine, rồi phân phối chúng vào các bộ xử lý logic và gắn mỗi bộ xử lý logic này với một thread hệ thống được tạo ra trước đó để thực thi các goroutine này. Nói cách khác, mỗi thread hệ thống sẽ xử lý một nhóm goroutine được điều phối thông qua bộ xử lý logic. Với bộ điều khiển quản lý tác vụ đồng thời và cơ chế bộ xử lý logic, những cái khó khăn, phức tạp khi khai báo thread Go đã xử lý hết giúp chúng ta rồi, chúng ta chỉ việc sử dụng thôi. Để khởi tạo một goroutine ta chỉ cần thêm phía trước một function call hay method call từ khoá Hiển thị số từ 1 đến 20ví dụ trên có 2 hàm là Do hàm g1() viết trước hàm g2() nên g1() sẽ thực thi trước g2(), khi đó g2() sẽ phải chờ g1() thực thi xong và kết quả sẽ hiển thị các số từ 1 đến 20 một cách tuần tự. Kết quả sau khi chạy chương trìnhVD: hiển thị số từ 1 đến 20 sử dụng goroutines
Ví dụ trên có 2 hàm là g1() và g2() ta thêm từ khóa go đằng trước tên hàm để thể hiện đây là hàm goroutine, khi đó 2 hàm g1() và g2() sẽ được thực thi đồng thời mà không cần phải chờ hàm trước nó thức thi xong, ta thêm từ khóa go vào trước các câu lệnh Mỗi lần chạy, các kết quả, thứ tự các chữ số cho ra là khác nhau. Ưu điểm khi sử dụng goroutines
Kết bàiTrên đây là bài giới thiệu cơ bản nhất về goroutines, tuỳ vào mục đích của chương trình mà các bạn có thể quyết định có sử dụng goroutines hay không. Hế nhô các bạn, sau một thời gian làm việc với NodeJS thì mình biết đến go một cách tình cờ qua một lần đi phỏng vấn. Và bây giờ mình đã đến với Go bằng tất cả tâm tư, cũng như những kinh nghiệm khi mình đã làm việc với NodeJS 10 Add-on Google Sheets phải có dành cho các Recruiters 10 Công cụ Go-To Tech dành riêng cho các Software Developer Ngày Đầu học Go, đọc đâu cũng thấy Go Channel is awesome, Go is concurrency, the modern programming language…, thật là thú vị, google vài vòng mình cx nhanh chóng nhận ra một điểm mà Go làm tốt hơn NodeJS là create Worker process, channel và goroutine là nguyên nhân khiến việc kiểm soát Worker dễ hơn NodeJS rất nhiều, ví dụ để giới hạn n Worker cùng nhau chạy đồng thời chúng ta chỉ cần tạo cho nó một buffered channel sau đó chạy n Worker trong n goroutine. // create a buffered channel var UploadPool chan WorkerMessage // worker subcribe to channel and do something with it Sleep for example func Worker(UploadPool chan WorkerMessage){ for { workerMessage := <- UploadPool time.Sleep(time.Second) } } // init function to run n worker in goroutine func InitWorker(numWorker int, UploadPool chan WorkerMessage){ for i:=1;i<=numWorker;i++{ go Worker(UploadPool) } } Và tiếp theo đây, nhân dịp mình đang dùng NodeJS để xử lí upload ảnh, và resize ảnh nên mình sẽ làm điều tương tự với GoLang để xem chúng nó có gì hay. Follow Chart Nào cùng bắt đầu với Follow 1: Lắng nghe form-data request từ front end và save file to local. Mình sẽ sử dụng Gin Framework, một web framework nhiều sao nhất trên github. func HandleUploadForm(c *gin.Context) { form,_ := c.MultipartForm() files := form.File["files"] var fileNames []string for _, file := range files { fileName :=fmt.Sprintf("%v",uuid.New()) + file.Filename fileNames = append(fileNames,fileName) c.SaveUploadedFile(file, "storage/image/"+fileName) } c.JSON(200, utils.ApiResponse{ Ok: true, Message: "Uploaded successfully.....", Data: fileNames, }) for _,fileName := range fileNames{ // loop and push WorkerMessage to UploadPool for Worker. UploadPool <- WorkerMessage{FileName: fileName,Resize:true} } } Như trong đoạn code đầu chúng ta đã có n Worker chạy concurrency và đang trong trang thái chờ nhận file để upload lên GCloud. Thêm cho nó 1 function để nó có thể upload lên GCloud nào. Thêm channel finished cho mỗi goroutine để có thể hứng được kết quả của mỗi task upload, tuỳ vào kết quả và theo logic của mỗi người mà chúng ta sẽ xử lí tiếp. func uploadToGCloudStorage(fileName string, finished chan bool) { f, err := os.Open("storage/image/" + fileName) if err != nil { utils.Logger("error", err) return } defer f.Close() object := Bucket.Object(fileName) wc := object.NewWriter(context.Background()) if _, err = io.Copy(wc, f); err != nil { utils.Logger("error", err) return } if err := wc.Close(); err != nil { utils.Logger("error", err) return } fmt.Println(utils.ApplyStyle("bold", "yellow", "Upload to Cloud successfully....")) object.ACL().Set(context.Background(),storage.AllUsers,storage.RoleReader) objectAddress := ObjectAddress{ ID:primitive.NewObjectID(), FileName: fileName, } _,err = database.Models.Object.InsertOne(context.Background(),objectAddress) if err != nil { utils.Logger("error",err) } finished <- true } func UploadImageTaskConsumer(UploadPool chan WorkerMessage){ for { workerMessage := <- UploadPool finished := make(chan bool) if workerMessage.Resize == true { go ImageAnalysis(ObjectAddress{ FileName: workerMessage.FileName, }) } go uploadToGCloudStorage(workerMessage.FileName,finished) <- finished } } Vậy là xong vụ upload file, so sánh với NodeJS thì thay vì phải quản lí Worker rất vất vả nodejs thì giờ đây, chúng ta có thể làm điều đó dễ dàng với Go Channel. Các bạn có thể xem responstory của mình tại đây, mình mong được các bạn góp ý để cải thiện bản thân, trở nên tốt hơn. |