Skip to main content

Các khái niệm cơ bản về Grand Central Dispatch (GCD)


Khái niệm về GCD mình sẽ ko nhắc lại nữa. Bài viết hôm nay mình chỉ muốn nêu ra 1 số định nghĩa rõ ràng cho người mới học về GCD. Để nắm rõ về GCD các bạn cần hiểu các khái niệm sau đây:
  1. Serial Queue và Concurrent Queue
  2. Thread
  3. Các hàng đợi của GCD
  4. Sync and Async
  5. Không chạy DispatchQueue.main.sync trên main thead
  6. Các ví dụ về GCD

Hàng đợi là tập hợp các task , chạy trên chính luồng chính hoặc chạy trên nền.
Task theo nghĩa đen là 1 khối code được gán cho hàng đợi và nó có thể được sử dụng nhiều lần(được sử dụng lại). Việc thực hiện các task phải tuân theo quy tắc FIFO(First in, First out) .
Một hàng đợi chứa nhiều task , mỗi task có thể thực thi theo kiểu serial hoặc concurrent. Một chuỗi các task được thực hiện serial , nếu task này được thực hiện xong thì các task khác mới được thưc hiện. Mặt khác 1 chuỗi các task được thực hiện Concurrent nếu nhiều task cùng thực thi cùng 1 lúc.

1. Serial Queue và Concurrent

Serial queue:

  • Thưc hiện các task một cách tuần tư. Một task chỉ được thực hiện khi mà task phía trước nó trong hàng đợi đã thực hiện xong và kết thúc. Thứ tự các task được nhập vào hàng đợi chính cũng chính là thứ tự các task được thực hiện và kết thúc.

Concurrent queue:

  • Các task được thưc hiện 1 cách đồng thời.Khi các task nhập vào hàng đợi thì các task này có thời gian bắt đầu thực hiện gần như bằng nhau , nhưng tuỳ thuộc vào mỗi task mà chúng có thời gian hoàn thành khác nhau.
Note: Mỗi 1 ô vuông được đánh số thứ tự là 1 task. Mỗi 1 task theo nghĩa đen chính là 1 khối block code.

2. Thread là gì

Hiểu đơn giản nó là các luồng để thực hiện các task trong hàng đợi. Vậy serial, concurrent queue có mấy luồng? Serial queue chỉ có 1 luồng duy nhất còn concurrent queue thì có nhiều luồng.
Nhìn trên hình: Nếu queue là concurrent queue và có 2 thread thì chương trình sẽ chia các task trong hàng đợi cho 2 thread 1 và 2 ,để các thread thực hiện các task.

3. Các hàng đợi của GCD

  1. Main queue( nó chính là serial queue nó chạy các task trên main thread): Sử dụng để cập nhật giao diện người dùng sau khi hoàn thành công việc trong 1 task của concurrent queue.
  2. Global queue( là concurrent queue): Sử dụng để thực hiện các công việc không phải giao diện người dùng dưới background.
  3. Customer queue(nó có thể là serrial queue hoặc concurrent queue)

Ví dụ:


4. Sync and Async

  • Sync: Thực thi các tác vụ một cách đồng bộ với hàng đợi hiện tại. Current queue sẽ bị block để task trên different queue thực thị.
Mình sẽ viết 1 ví dụ để các bạn hiểu rõ hơn:
Hàng đợi queue chạy với phương thức sync:
Chương trình sẽ dừng task B đang chaỵ trên main thread để cho task A đang chạy trong hàng đợi queue thực hiện vì nó đang chạy sync. Kết quả:
  • Async: chạy bất đồng bộ với hàng đợi hiện tại, xử lý công việc song song với hàng đợi hiện tại.Nghĩa là chương trình sẽ trả điều khiển về hàng đợi hiên tại ngay lập tức sau khi mà khởi chạy task trên hàng đợi khác mà không chờ task trên hàng đợi hiện tại kết thúc. Nếu chúng ta thay đổi hàng đợi queue chạy với phương thức async: thì chương trình sẽ chaỵ task A trên hàng đợi queue và task B trên main thread cùng 1 lúc:
Kết quả:

5. Không chạy DispatchQueue.main.sync trên main thead!

Khi chúng ta gọi phương thức sync trên main thread thì ứng dụng sẽ bị crash app , vì DispatchQueue.main là 1 serial queue nó chỉ có duy nhất 1 thread . Khi gọi phương thức sync thì chương trình sẽ block luồng chính, luồng chính sẽ đợi các task thực hiện xong nhưng các task sẽ không bao giờ hoàn thành vì nó sẽ không thể bắt đầu do hàng đợi bị block ⇒ hiện tượng deadlock. Mình sẽ nêu ra 1 ví dụ về deadlock:
In Apple docs, it says: Important: You should never call the dispatch_sync or dispatch_sync_f function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.
“Important: Bạn không bao call dispathch_sync từ một task đang thực hiện trong cùng một hàng đợi. Điều này quan trong đặc biệt quan trọng đối với serial queue vì dẫn đến hiện tượng deadlock nhưng cũng nên tránh đối với concurrent queue. ”

6. Các ví dụ về GCD

Mình sẽ nêu ra 1 số ví dụ các bạn hãy tự đoán kết quả để hiểu hơn về GCD nhé:
VD1: Hỏi: Khi mình tạo 1 concurrent queue chạy với phương thức async thì nó sẽ in ra lần lượt các giá trị của i hay in ra 1 cách lộn xộn ?
Trả lời: Tuy đây là concurrentQueue có nhiều thread để chạy các task song song cho thực hiện với phương thức async nhưng các giá trị i vẫn in ra lần lượt đơn giản là cả vòng lặp for chỉ là 1 task duy nhất. Câu hỏi này mình đã từng bị các anh phỏng vấn hỏi, đây chỉ là câu hỏi mẹo 😄.
VD2: Hỏi : khi chạy như code bên dưới nó sẽ in ra như thế nào? Trả lời: Câu này dễ phải không mình có giải thích 1 chút ở phần Sync, các bạn tự trả lời nhé.
VD 3: Hỏi : 2 hàng đợi firstQueue và secondQueue có độ ưu tiên bằng nhau cùng chạy với phương thức sync thì kết quả sẽ in ra như thế nào Kết quả : các bạn tự viết code run nhé Trả lời: Vì chạy với phương thức sync nên hàng đợi secondQueue sẽ bị block để hàng đợi firstQueue thực hiện task trước sau khi task firstQueue thì chương trình mới chạy task secondQueue.
Cũng đoạn code trên nhưng mình thay thành async thì sao: Trả lời : Các bản hãy tự nghĩ và tự giải thích xem, đơn gian mà đúng ko !
VD4: cũng Ví dụ như trên nhưng mình thay đổi độ ưu tiên, Các bạn có bao giờ tự hỏi mình độ ưu tiên của các các hàng đợi nó có ý nghĩa gì
Kết quả: Trả lời: Hàng đợi thứ hai có độ ưu tiên cao hơn hàng đợi thứ nhất, hệ thống sẽ cung cấp tài nguyên cho hàng đợi thứ hai vì nó được đánh dấu là hàng đợi quan trọng . Một khi hàng đợi thứ hai hoàn thành thì hệ thông sẽ cung cấp tài nguyên cho hàng đợi thứ nhất.
Bạn viết tuy còn sơ sài nhưng cũng là 1 tài liệu cho những bạn mới tiếp xúc về GCD hiểu và nắm rõ kiến thức cơ bản để học tiếp các phần DispatchGroup, NSOperation.
**Tài liệu tham khảo: **

Comments

Popular posts from this blog

Swift GCD part 1: Thread safe singletons

Preview Singletons are entities, referenced to the same instance of a class from everywhere in your code. It doesn't matter if you like them or not, you will definitely meet them, so it's better to understand how they work. Constructing and handling a set of data doesn't seem to be a big challenge at first glance. The problems appear when you try to optimise the user experience with background work and your app starts acting weird. ??‍♂️ After decades of watching your display mostly with a blank face, you finally realize that your data isn't handled consistently by the manager because you're accessing it (running tasks on it) from multiple threads at the same time. So you really do have to deal with making your singletons thread safe. This article series is dedicated to thread handling using Swift. In the first part below you will get a comprehensive insight into som...

Kiến thức cơ bản về RxSwift

Bài viết với mong muốn cung cấp thông tin cơ bản về kiến trúc, các thuật ngữ được sử dụng phổ biến về RxSwift, giúp những lập trình viên lần đầu làm quen RxSwift sẽ trở nên dễ dàng hơn. Trong bài viết có sử dụng một số từ khóa tiếng Anh, mình xin phép sẽ giữ nguyên bản không sử dụng tiếng Việt vì có lẽ sẽ dễ hiểu hơn cho người đọc. Observable Sequences Mọi hoạt động trong RxSwift từ việc đăng ký và xử lý sự kiện đều thông qua một Observable Sequences Trong RxSwift , các kiểu dữ liệu như Arrays , Strings hoặc Dictionary sẽ được convert sang Observable Sequences . Ta có thể tạo ra "Observable Sequences" của bất kỳ kiểu đối tượng nào tuân theo Sequence Protocol của Swift Standard Library . let helloSequence = Observable.just( "Hello Rx" ) let fibonacciSequence = Observable. from ([ 0 , 1 , 1 , 2 , 3 , 5 , 8 ]) let dictSequence = Observable. from ([ 1 : "Hello" , 2 : "World" ]) Đăng ký nhận event từ ""Observable Se...

Thread safe singleton’s in Swift

What are singletons? — Singleton is design patterns which says that there should be only one instance of the class for the lifetime of the application. One the best example of Singleton is AppDelegate . How to write a singleton class ? class DefaultDict{ private var dict:[String:Any] = [:] public static let sharedManager = DefaultDict() private init(){ } public func set(value:Any,key:String){ dict[key] = value } public func object(key:String) -> Any?{ dict[key] } public func reset(){ dict.removeAll() } }   Testing singleton class under concurrent circumstances. We are going to write an example where we will set values in dict from various threads and even try to access some with different threads. When we do this we will encounter a crash. If you look closely it will be because of race condition and the crash will be on line set(value:Any,key:String) . class ViewController: UIViewController { ...