kotlin Concurrency ตอนที่ 1 processes threads และ kotlin coroutines

kotlin coroutines ,asynchronous คืออะไร

minatorak
odds.team
Published in
4 min readDec 19, 2019

--

Process

คือ program in execution และทุก processes ใช้อย่างน้อย 1 threads หรือมากกว่าในการทำงาน และ application นึงอาจมีหลาย process เช่น processId

Thread

เอาไว้เก็บ instructions สำหรับให้ processor ทำงาน โดยทุก thread สามารถแก้ไข resources ที่มีใน processได้ แต่ก็มี storage ของตัวเองโดยเฉพาะเช่นกัน เรียก thread-local storage ซึ่งจะเกี่ยวข้องกับ thread-safe อีกที เช่น

function main() // main thread

Graphic User Interface (GUI) // UI thread

thread-blocking // เมื่อ instructions ที่ทำงานใช้เวลานานเกินไป

Coroutines

มันถูกนิยามว่าเป็น lightweight threads เพราะ define the execution of a set of instructions for a processor to execute.Also, coroutines have a similar life cycle to that of threads.

1 coroutine ทำงานบน 1 thread เสมอ โดย 1 thread สามารถมีได้หลาย coroutine

coroutine สามารถย้ายไปมากับ thread อื่นๆได้ เช่น ถูกสร้างโดย thread a เริ่มทำงานบน thread b แล้วทำงานเสร็จตอน thread a

ในช่วงเวลานึง Thread นึงสามารถ execute ได้แค่ coroutine เดียวถึงแม้จะ stack coroutine ได้หลายอันก็ตาม แต่หยิบมาทำได้แค่อันเดียว

โดยปกติ coroutine ใช้เพื่อจัดการ concurrency ระหว่างหลายๆ thread ง่ายขึ้น และจัดการ Asynchronous กับ Synchronous

concurrency vs parallelism

หลักการสำคัญของสองเรื่องนี้คือ ใช้ในการ “แบ่งงาน” และแตกต่างกันที่ “จำนวน” ที่ทำ

ขอเริ่มเล่าจาก non-concurrent อย่าง Sequential ก่อนโดย code จะทำงานไปตามลำดับ

concurrency on sigle core

Parallélisme on multiple core

Sequential คือการทำงานหลายๆงานตามลำดับก่อนหลัง

Concurrent คือการสลับทำงานหลายๆอย่างในช่วงเวลาเดียวกัน

Parallelism คือการกระจายทำงานหลายๆอย่างในช่วงเวลาเดียวกัน

“Concurrency is about dealing with a lot of things at once” — Rob Pike

“Parallelism is about doing a lot of things at once ” — Rob Pike

Book: Learning Concurrency in Kotlin

ปัญหาของ concurrency vs parallelism

Race conditions

Atomicity violation

Deadlocks

Livelocks

Garden by the Bay, Singapore Photo by Zhu Hongzhi on Unsplash

Concepts เบื้องหลัง

Suspending computation

Suspending functions

Suspending lambdas

Coroutine dispatcher

Coroutine builders

Suspending computation

มองว่าเป็นกลุ่ม code ชุดนึงที่สามารถ suspend การ execution ได้โดยที่ไม่ blocking the thread ที่ทำงานอยู่ซึ่ง computation นี้จะทำงานได้ภายใต้ suspending functions เท่านั้น

Suspending functions

how it work

Suspending lambdas

คล้ายกับ suspending function เป็น anonymous local function แต่ว่าจะสั่ง suspend การ execution ได้โดย suspending function อื่น

Coroutine dispatcher

ทุกครั้งที่ coroutine start หรือ resume บน thread ใดๆและทำงานแบบไหนบน thread จะถูกกำหนดโดย CoroutineDispatcher interface

  • Dispatchers.Default หากไม่มีการกำหนด Context โดย ContinuationInterceptor จะ shared pool of threads บน JVM โดยเรียกใช้ threadsได้มากสุดตามจำนวน CPU cores และขั้นต่ำ 2 thread
  • Dispatchers.IO ถูกออกแบบมาสำหรับ IO tasks ที่มักจะ loading กับ blocking หนักๆ เข้าใจว่า created and shutdown ได้เองและ dispatcher ตัวนี้ shared pool of threads ร่วมกับแบบ Default
  • Dispatchers.Unconfined เป็นการ dispatcher โดยไม่เจาะจง thread ที่ทำงาน โดยจะ initial coroutine ที่ current thread เสมอแต่ resume บน thread ใดก็ได้ และเนื่องจากไม่ specific thread เพราะฉะนั้น nested coroutines ใน dispatcher นี้จะทำตาม concept event-loop เพื่อหลีกเลี่ยงการ stack overflows

Coroutine builders

เป็น function suspending lambda ที่ provide สร้าง coroutine

  • coroutineScope ใช้กำหนดขอบเขตในการสร้าง coroutines ของทุกๆ coroutine builder อย่างเช่น ( async, launch) จะทำงานโดยกำหนด coroutineScope ไหนและ coroutineContext ใด
  • GlobalScope เป็น global CoroutineScope โดยทำงาน top-level coroutines เช่น การใช้ launch(Dispatchers.Default) { … } กับ GlobalScope.launch { … } หรือ GlobalScope.async กับ async(Dispatchers.Default) โดย scope อื่นๆนอกจาก GlobalScope ให้ใช้ Dispatchers.Unconfined
  • MainScope เป็น coroutineScope สำหรับ UI components.
  • runBlocking มักจะใช้ห่อ suspend code บน blocking code โดยจะทำการ block thread ปัจจุบันจนกว่า execution ของ coroutine เสร็จ เข้าใจง่ายๆคือ ใช้จองพื้นที่บน synchronous code ให้ข้างในเป็น asynchronous code โดยเมื่อ asynchronous ทั้งหมดทำงานเสร็จแล้ว จะกลับไปรัน synchronous เดิมต่อ
  • async ใช้เมื่อต้องการ return result ออกมา แต่ถ้าเกิด exception จะreturn result exception ออกมาในรูปแบบ Deferred<T>
  • launch จะไม่ return result ออกมา แต่จะได้ Job สำหรับ control execution

การ Shared state and concurrency

  • Channels: ใช้รับและส่งข้อมูลระหว่าง coroutines
  • Worker pools: pool of coroutines ที่แจกจ่ายไป processing ที่ threads ต่างๆ
  • Actors: มีไว้ encapsulate state และ channel ให้ปลอดภัยมากขึ้น เมื่อ coroutine ทำงานบน thread ที่ต่างกันแต่ share state กัน
  • Mutual exclusions (Mutexes)
  • Thread confinement

Reference & interesting

--

--