Concurrency Programming Information

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

Lập trình đồng bộ là 1 kỹ thuật lập trình trung cấp. Để hiểu được bạn cần nên quen thuộc có những API bất đồng bộ như URLSession, và dễ dàng viết và dùng những completion handler closures. Giả dụ 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 nên hiểu hết vấn đề.

Chi tiết lịch sử

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

Để xử lý vấn đề khó khăn này, cả iOS và OSX đề ra 1 phương pháp tiếp cận khác cho việc xử lý đồng thời đấy là: Thay thế vì nên tạo những threads 1 phương pháp quản lý, những ứng dụng chỉ đơn giản là gửi những job vào những hàng đợi Queue. Còn việc khởi tạo những thread thế nào, bao nhiêu thread được đẩy cho hệ thống quyết định. Bằng phương pháp để cho hệ thống quản lý quản lý những thread, những ứng dụng có thể đạt được 1 mức độ linh hoạt mà phương pháp xử lý cũ ko bao giờ đạt được. Đồng thời lập trình viên có được 1 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 job được xử lý cùng 1 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 cường độ xử lý, tận dụng cao nhất} sức mạnh của kiến trúc chip đa nhân.

Giả dụ chúng ta xử lý 1 job non-UI nặng trên principal thread, job này sẽ block lại principal thread và app của chúng ta ko thể tiếp nhận được những tương tác của khách hàng nữa. Concurrency Programming Guide

Lúc này chúng ta cần chuyển những tác vụ nặng non-UI job sang 1 thread khác để xử lý, và principal thread sẽ tiếp tục làm cho những nhiệm vụ khác trong đấy có nhiệm vụ quan yếu nhất là đón nhận những tương tác của khách hàng. Concurrency Programming Guide

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

Concurrency

Concurrency ko chỉ là 1 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. Concurrency Programming Guide

Xem Thêm  Chứng chỉ TKT là gì? Nên học TKT hay TESOL? – EduLife

Queue

Queue là hàng đợi những công việc, hoạt động theo nguyên tắc FIFO, job nào vào trước thì sẽ được thực hành trước, job nào vào sau sẽ được thực hành sau. Có 2 loại hàng đợi: Serial Queue: là hàng đợi thực hành theo tuần tự động. Trong 1 thời điểm chỉ có 1 job được thực thi. Lúc nào job này thực thi xong thì job khác new khởi đầu. Dí dụ tiêu biểu của hàng đợi này là Most important thread. Concurrency Programming Guide

Concurrent Queue: là hàng đợi thực hành đồng thời. Trong 1 thời điểm có thể có nhiều job được thực hành cùng 1 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 hartware thực tế để khởi tạo và cấp phát những Thread để xử lý những tác vụ. Concurrency Programming Guide

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

Synchronous và Asynchronous

Đầu vào của những queue là những closure. Những closure này được đánh dấu về phương pháp thức thực hành nó trước lúc gửi tới 1 queue Có 2 phương pháp thức thực hành của 1 closure: Giả dụ job đánh dấu là Synchronous thì job này sẽ block lại queue mà nó được gọi, ko cho phép queue đấy thực thi thêm job nào khác trong thời kì nó đang chạy. Giả dụ job được đánh dấu là Asynchronous thì job này được gọi và ngay tiếp theo nó trả quyền điều khiển cho hàm gọi nó và hàng đợi sẽ thực thi 1 closure tiếp theo (trường hợp có đủ queue để thực thi).

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

Synchronous, Asynchronous là phương pháp thức thực hành của 1 job. Serial Queue, Concurrent Queue là đích tới của job đấy.

Synchronous, Asynchronous nói cho bạn biết là queue hiện thời có nên đợi job hoàn thành rồi new gọi job new hay ko Serial Queue, Concurrent Queue thì cho bạn biết là có queue hiện thời, bạn có 1 thread hay nhiều thread. 1 Job được thực hành 1 lúc hay nhiều job được thực hành đồng thời.

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

func simpleQueues() { let queue = DispatchQueue(label: “com.bigZero.GCDSamples”) sentayho.com.vn { for i in 0..<5 { print(“🔵 (i) -( sentayho.com.vnent))”) } } sentayho.com.vn { for i in 0..<5 { print(“⚾️ (i) – (Thread.present))”) } } for i in 0..<10 { print(“❤️ (i) – (Thread.present)”) } }

Concurrency Programming Guide lúc gửi 1 async job in 🔵 vào trong queue, ngay tức thời nó trả quyền điều khiển cho operate gọi nó. Vì vậy chúng ta tiếp tục chạy được dòng lệnh gửi 1 async job thứ 2 vào trong queue – job in ⚾️ async job thứ 2 sau thời điểm được đẩy vào trong Queue, nó ngay tức thời trả điều khiển lại cho operate gọi nó và operate simpleQueues() tiếp tục thực thi việc in ❤️ Do job 🔵 và job ⚾️ được đưa vào cùng 1 Serial Queue nên nó được chạy trên 1 thread theo phương pháp thức tuần tự động. Job ❤️được thực hành trên thread hiện thời là principal thread. (🔵 tuần tự động ⚾️) // Most important thread Job Vì principal thread có mức độ ưu tiên cao nhất nên trong quy trình thực hành, mặc dầu số ❤️ bằng tổng số 🔵 + ⚾️. Nhưng lúc thực hành ❤️ vẫn thực hành xong trước 2 job kia

🔵 0 -<NSThread: 0x610000078200>{quantity = 3, title = (null)}) ❤️ 0 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} ❤️ 1 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} 🔵 1 -<NSThread: 0x610000078200>{quantity = 3, title = (null)}) ❤️ 2 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} 🔵 2 -<NSThread: 0x610000078200>{quantity = 3, title = (null)}) ❤️ 3 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} ❤️ 4 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} 🔵 3 -<NSThread: 0x610000078200>{quantity = 3, title = (null)}) ❤️ 5 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} 🔵 4 -<NSThread: 0x610000078200>{quantity = 3, title = (null)}) ❤️ 6 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} ❤️ 7 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} ⚾️ 0 – <NSThread: 0x610000078200>{quantity = 3, title = (null)}) ❤️ 8 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} ❤️ 9 – <NSThread: 0x610000070d80>{quantity = 1, title = principal} ⚾️ 1 – <NSThread: 0x610000078200>{quantity = 3, title = (null)}) ⚾️ 2 – <NSThread: 0x610000078200>{quantity = 3, title = (null)}) ⚾️ 3 – <NSThread: 0x610000078200>{quantity = 3, title = (null)}) ⚾️ 4 – <NSThread: 0x610000078200>{quantity = 3, title = (null)})

Xem Thêm  [Snapshot Là Gì] – Tổng quan về Snapshot trong cơ sở dữ liệu

Trường hợp gửi 2 job Sync vào Serial Queue

func simpleQueues() { let serialQueue = DispatchQueue(label: “com.bigZero.GCDSamples”) sentayho.com.vn { for i in 0..<5 { print(“🔵 (i) -( sentayho.com.vnent))”) } } sentayho.com.vn { for i in 0..<5 { print(“⚾️ (i) – (Thread.present))”) } } for i in 0..<10 { print(“❤️ (i) – (Thread.present)”) } }

Lúc gửi sync job 🔵 vào serialQueue, job 🔵 block lại operate simpleQueues ko cho thực hành tiếp những tác vụ tiếp theo. Lúc này principal thread sẽ được rảnh (vì principal queue đang bị block lại), vì vậy nó được cấp phát để thực hành job 🔵. Sau thời điểm thực hành xong job 🔵, job ⚾️ khởi đầu được gọi và cũng tiếp tục như trên. cuối cùng job ❤️ được gọi trên principal queue, và có hầu hết những job vụ trên principal queue đều sẽ được ưu tiên thực hành trên principal thread.

🔵 0 -<NSThread: 0x610000073200>{quantity = 1, title = principal}) 🔵 1 -<NSThread: 0x610000073200>{quantity = 1, title = principal}) 🔵 2 -<NSThread: 0x610000073200>{quantity = 1, title = principal}) 🔵 3 -<NSThread: 0x610000073200>{quantity = 1, title = principal}) 🔵 4 -<NSThread: 0x610000073200>{quantity = 1, title = principal}) ⚾️ 0 – <NSThread: 0x610000073200>{quantity = 1, title = principal}) ⚾️ 1 – <NSThread: 0x610000073200>{quantity = 1, title = principal}) ⚾️ 2 – <NSThread: 0x610000073200>{quantity = 1, title = principal}) ⚾️ 3 – <NSThread: 0x610000073200>{quantity = 1, title = principal}) ⚾️ 4 – <NSThread: 0x610000073200>{quantity = 1, title = principal}) ❤️ 0 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 1 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 2 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 3 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 4 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 5 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 6 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 7 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 8 – <NSThread: 0x610000073200>{quantity = 1, title = principal} ❤️ 9 – <NSThread: 0x610000073200>{quantity = 1, title = principal}

Trên sự thật} chúng ta thay thế đổi 1 chút, job thứ 2 chúng ta chuyển thành async

func simpleQueues() { let serialQueue = DispatchQueue(label: “com.bigZero.GCDSamples”) sentayho.com.vn { for i in 0..<5 { print(“🔵 (i) -( sentayho.com.vnent))”) } } sentayho.com.vn { for i in 0..<5 { print(“⚾️ (i) – (Thread.present))”) } } for i in 0..<10 { print(“❤️ (i) – (Thread.present)”) } }

cũng như tại trên Lúc gửi sync job 🔵 vào serialQueue, job 🔵 block lại operate simpleQueues ko cho thực hành tiếp những tác vụ tiếp theo. Lúc này principal thread sẽ được rảnh (vì principal queue đang bị block lại), vì vậy nó được cấp phát để thực hành job 🔵. Sau thời điểm thực hành xong job 🔵, job ⚾️ khởi đầu được gọi. Do job ⚾️ là async nên ngay tức thời nó trả lại quyền điều khiển cho operate gọi nó và job ❤️ được gọi ưu tiên trên principal thread. Do principal thread đang được dùng rồi, nên hệ thống cấp phát cho job ⚾️ 1 thread khác để xử nguyên nhân đấy ta có kết quả

🔵 0 -<NSThread: 0x6100000757c0>{quantity = 1, title = principal}) 🔵 1 -<NSThread: 0x6100000757c0>{quantity = 1, title = principal}) 🔵 2 -<NSThread: 0x6100000757c0>{quantity = 1, title = principal}) 🔵 3 -<NSThread: 0x6100000757c0>{quantity = 1, title = principal}) 🔵 4 -<NSThread: 0x6100000757c0>{quantity = 1, title = principal}) ⚾️ 0 – <NSThread: 0x60800007a980>{quantity = 4, title = (null)}) ❤️ 0 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ⚾️ 1 – <NSThread: 0x60800007a980>{quantity = 4, title = (null)}) ❤️ 1 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ❤️ 2 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ⚾️ 2 – <NSThread: 0x60800007a980>{quantity = 4, title = (null)}) ❤️ 3 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ❤️ 4 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ⚾️ 3 – <NSThread: 0x60800007a980>{quantity = 4, title = (null)}) ❤️ 5 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ⚾️ 4 – <NSThread: 0x60800007a980>{quantity = 4, title = (null)}) ❤️ 6 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ❤️ 7 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ❤️ 8 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal} ❤️ 9 – <NSThread: 0x6100000757c0>{quantity = 1, title = principal}

Xem Thêm  Nhận xét home windows 10 enterprise là gì

Trường hợp gửi 3 job Async vào Concurrent Queue

func concurrentQueues() { let concurrentQueue = sentayho.com.vnal() sentayho.com.vn { for i in 0..<10 { print(“🔵 (i) – (Thread.present)”) } } sentayho.com.vn { for i in 0..<10 { print(“❤️ (i)- (Thread.present)”) } } sentayho.com.vn { for i in 0..<10 { print(“⚾️ (i)- (Thread.present)”) } } }

Do 3 job đều là async nên cả 3 job đều được đưa vào trong concurrentQueue. Lúc này hệ thống sẽ cấp phát cho concurrentQueue 3 thread để thực hành đồng thời 3 job trên 3 thread khác nhau do đấy ta được

🔵 0 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 0- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 0- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 1 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ⚾️ 1- <NSThread: 0x608000066880>{quantity = 1, title = principal} ❤️ 1- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 2- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 2 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 2- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 3- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 3 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 3- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 4- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 4 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 4- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 5- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 5 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 5- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 6- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 6 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 6- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 7- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 7 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 7- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 8- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 8 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 8- <NSThread: 0x600000069500>{quantity = 5, title = (null)} ⚾️ 9- <NSThread: 0x608000066880>{quantity = 1, title = principal} 🔵 9 – <NSThread: 0x600000069480>{quantity = 3, title = (null)} ❤️ 9- <NSThread: 0x600000069500>{quantity = 5, title = (null)}

Thay thế đổi 1 chút. Ta cho job ❤️ phát triển thành sync. Lúc add xong job ❤️, do job này là sync nên nó khoá queue lại, ko cho add job ⚾️ vào nữa. Sau thời điểm job ❤️ chạy xong, job ⚾️ new được add vào concurrent Queue. Do vậy như kết quả, job ⚾️ chạy 1 mình cuối cùng.

❤️ 4- <NSThread: 0x610000065980>{quantity = 1, title = principal} 🔵 4 – <NSThread: 0x618000064380>{quantity = 3, title = (null)} ❤️ 5- <NSThread: 0x610000065980>{quantity = 1, title = principal} 🔵 5 – <NSThread: 0x618000064380>{quantity = 3, title = (null)} ❤️ 6- <NSThread: 0x610000065980>{quantity = 1, title = principal} ❤️ 7- <NSThread: 0x610000065980>{quantity = 1, title = principal} 🔵 6 – <NSThread: 0x618000064380>{quantity = 3, title = (null)} ❤️ 8- <NSThread: 0x610000065980>{quantity = 1, title = principal} 🔵 7 – <NSThread: 0x618000064380>{quantity = 3, title = (null)} ❤️ 9- <NSThread: 0x610000065980>{quantity = 1, title = principal} 🔵 8 – <NSThread: 0x618000064380>{quantity = 3, title = (null)} 🔵 9 – <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 0- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 1- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 2- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 3- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 4- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 5- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 6- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 7- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 8- <NSThread: 0x618000064380>{quantity = 3, title = (null)} ⚾️ 9- <NSThread: 0x618000064380>{quantity = 3, title = (null)}

1 lưu ý cuối cùng, chúng ta tạo queue, chúng ta có thể quyết định rằng queue này sẽ chạy trên 1 thread hay nhiều thread bằng phương pháp chỉ định loại queue là Serial hay Concurrent. Nhưng chính xác là thread nào thực thi những tác vụ thì chúng ta ko được quyền quyết định, việc đấy được đẩy cho hệ thống quyết định dựa trên những chi tiết hartware, và tải hiện thời của hệ thống.