12/08/2018, 15:05

Concurrency Programming Guide

Bài viết này dành cho ai? Lập trình đồng bộ là một kỹ thuật lập trình trung cấp. Để hiểu được bạn cần phải quen thuộc với các API bất đồng bộ như URLSession, và dễ dàng viết và sử dụng những completion handler closures. Nếu bạn chưa biết những vấn đề trên, bạn cũng có thể xem qua như 1 tài liệu ...

Bài viết này dành cho ai?

Lập trình đồng bộ là một kỹ thuật lập trình trung cấp. Để hiểu được bạn cần phải quen thuộc với các API bất đồng bộ như URLSession, và dễ dàng viết và sử dụng những completion handler closures. Nếu bạn chưa biết những vấn đề trên, bạn cũng có thể xem qua như 1 tài liệu tham khảo, và đừng bắt mình phải hiểu hết vấn đề.

Yếu tố lịch sử

Trong lịch sử phát triển máy tính, khối lượng công việc lớn nhất mà máy tính có thể xử lý trong một đơn vị thời gian được quyết định bởi tốc độ đồng hồ của CPU. Để tăng tốc CPU và thu nhỏ chip bán dẫn, người ta cố gắng nén một lượng lớn các đèn bán dẫn vào trong một diện tích nhỏ nhất. Tuy nhiên cuộc đua tăng tốc cho lõi CPU bị dừng lại do các giới hạn về phần cứng và nhiệt độ. Để tiếp tục tăng tốc cho CPU, người ta bắt đầu tìm một giải pháp khác để tăng tổng hiệu suất của CPU lên, đồng thời tăng hiệu suất tiêu thụ điện. Và giải pháp đó là đưa nhiều lõi hơn vào trong một CPU thay cho một lõi. Việc xử lý được đưa cho nhiều Core cùng xử lý, do đó tổng hiệu năng được tăng lên. Thoạt đầu, Giải pháp nghe có vẻ rất hay nhưng vấn đề lại nằm ở phần mềm. Để tận dụng được lợi thế xử lý nhiều core trong các ứng dụng thì không phải là đơn giản. Trong quá khứ, để sử dụng được các core này, chúng ta phải xử lý việc khởi tạo và quản lý các thread này một cách thủ công. Việc này thực sự khó khăn với hầu hết các lập trình viên, bởi việc xác định được con số tối ưu của các thread trong từng hoàn cảnh dựa trên khối lượng tải hệ thống hiện thời , và phần cứng ở dưới là không hề đơn giản.

Để xử lý vấn đề khó khăn này, cả iOS và OSX đề ra một cách tiếp cận khác cho việc xử lý đồng thời đó là: Thay vì phải tạo các threads một cách trực tiếp, các ứng dụng chỉ đơn giản là gửi các task vào các hàng đợi Queue. Còn việc khởi tạo các thread thế nào, bao nhiêu thread được đẩy cho hệ thống quyết định. Bằng cách để cho hệ thống quản lý quản lý các thread, các ứng dụng có thể đạt được một mức độ linh hoạt mà cách xử lý cũ không bao giờ đạt được. Đồng thời lập trình viên có được một mô hình lập trình đơn giản mà hiệu quả hơn.

Concurrency là gì?

Xử lý đồng thời - Concurrency - là việc nhiều task được xử lý cùng một lúc.

Tại sao app của chúng ta lại cần xử lý đồng thời - Concurrency?

  • Để giữ cho UI luôn trong trạng thái được đáp ứng.
  • Tăng tốc độ xử lý, tận dụng tối đa sức mạnh của kiến trúc chip đa nhân.

Nếu chúng ta xử lý một task non-UI nặng trên main thread, task này sẽ block lại main thread và app của chúng ta không thể tiếp nhận được những tương tác của người dùng nữa.

Lúc này chúng ta cần chuyển các tác vụ nặng non-UI task sang một thread khác để xử lý, và main thread sẽ tiếp tục làm các nhiệm vụ khác trong đó có nhiệm vụ quan trọng nhất là đón nhận những tương tác của người dùng.

Một số khái niệm của lập trình đồng thời

Concurrency

Concurrency không chỉ là một khái niệm cho thiết bị có chip nhiều nhân. Trong những thiết bị đơn nhân, chúng ta vẫn có thể xử lý được đa luồng dựa vào cơ chế time-slicing để chuyển ngữ cảnh.

Queue

Queue là hàng đợi các công việc, hoạt động theo nguyên tắc FIFO, task nào vào trước thì sẽ được thực hiện trước, task nào vào sau sẽ được thực hiện sau. Có hai loại hàng đợi: Serial Queue: là hàng đợi thực hiện theo tuần tự. Trong một thời điểm chỉ có 1 task được thực thi. Khi nào task này thực thi xong thì task khác mới bắt đầu. Ví dụ tiêu biểu của hàng đợi này là Main thread.

Concurrent Queue: là hàng đợi thực hiện đồng thời. Trong một thời điểm có thể có nhiều task được thực hiện cùng một lúc. Hệ thống sẽ tuỳ vào tải hiện thời của hệ thống và cấu hình phần cứng thực tế để khởi tạo và cấp phát các Thread để xử lý các tác vụ.

So sánh giữa Serial Queue và Concurrent Queue

Synchronous và Asynchronous

Đầu vào của các queue là các closure. Các closure này được đánh dấu về cách thức thực hiện nó trước khi gửi đến một queue Có hai cách thức thực hiện của một closure: Nếu task đánh dấu là Synchronous thì task này sẽ block lại queue mà nó được gọi, không cho phép queue đó thực thi thêm task nào khác trong thời gian nó đang chạy. Nếu task được đánh dấu là Asynchronous thì task này được gọi và ngay sau đó nó trả quyền điều khiển cho hàm gọi nó và hàng đợi sẽ thực thi một closure tiếp theo (nếu có đủ queue để thực thi).

Mối quan hệ giữa Synchronous, Asynchronous VS Serial Queue, Concurrent Queue

Synchronous, Asynchronous là cách thức thực hiện của 1 task. Serial Queue, Concurrent Queue là đích đến của task đó.

Synchronous, Asynchronous nói cho bạn biết là queue hiện thời có phải đợi task hoàn thành rồi mới gọi task mới hay không Serial Queue, Concurrent Queue thì cho bạn biết là với queue hiện thời, bạn có 1 thread hay nhiều thread. 1 Task được thực hiện 1 lúc hay nhiều task được thực hiện đồng thời.

Trường hợp gửi 2 async task vào serial queue

func simpleQueues() {
  let queue = DispatchQueue(label: "com.bigZero.GCDSamples")
  
  queue.async {
      for i in 0..<5 {
          print("            
0