- vừa được xem lúc

Thread.sleep() and kotlinx.coroutines.delay()

0 0 24

Người đăng: Phong Phung Ngoc

Theo Viblo Asia

Chào mọi người, trong lập trình Android, khi gặp một yêu cầu liên quan đến delay hoặc dừng một task vụ sau một khoảng thời gian chờ nhất định, chúng ta thường nghĩ đến 2 phương án là Thread.sleep() và kotlinx.coroutines.delay().

Vậy 2 cơ chế này có gì khác nhau và khi nào mình nên sử dụng chúng. Hôm nay mình sẽ xin chia sẻ tới mọi người ý hiểu của mình.

1. Khái niệm

kotlinx.coroutines.delay() và Thread.sleep() đều là cơ chế giới thiệu độ trễ hoặc tạm dừng trong quá trình thực thi chương trình, nhưng chúng được sử dụng trong các ngữ cảnh khác nhau và có các đặc điểm khác nhau.

1.1. kotlinx.coroutines.delay()

kotlinx.coroutines.delay() là một suspending function được cung cấp bởi thư viện Kotlin coroutines. Nó thường được sử dụng trong các asynchronous programming khi làm việc với các coroutine, nơi bạn muốn tạm dừng việc thực thi một coroutine mà không chặn luồng chính.

1.2. Thread.sleep()

Thread.sleep() là một static method được cung cấp bởi lớp Thread trong Java (cũng có thể sử dụng được trong Kotlin) và được sử dụng để tạm dừng quá trình thực thi của luồng hiện tại.

Thread.sleep() không chỉ tạm dừng việc thực thi một coroutine mà còn chặn toàn bộ luồng, nghĩa là các luồng khác trong ứng dụng sẽ không thể tiếp tục công việc của chúng trong suốt thời gian Sleep.

2. Ví dụ triển khai

2.1. kotlinx.coroutines.delay()

fun main(args: Array<String>) { runBlocking { run() } }
} suspend fun run() { coroutineScope { val timeInMillis = measureTimeMillis { val mainJob = launch { //Job 0 launch { print("A->") delay(1000) print("B->") } //Job 1 launch { print("C->") delay(2000) print("D->") } //Job 2 launch { print("E->") delay(500) print("F->") } //Main job print("G->") delay(1500) print("H->") } mainJob.join() } val timeInSeconds = String.format("%.1f", timeInMillis/1000f) print("${timeInSeconds}s") }
}

Luồng chạy sẽ như nhau:

Main Job sẽ chạy và sẽ bị tạm dừng bởi delay.

Tiếp đó là Job 0 -> Job 1 -> Job 2, 3 jobs đều khởi động cùng một lúc và bị delay với thời gian tương ứng trong từng job.

Tiếp theo, task có delay() time ngắn nhất thì chạy xong trước, theo sau là các công việc tiếp theo hoàn thành. Độ trễ dài nhất là 2s.

Kết quả là:

G->A->C->E->F->B->H->D->2.0s

or

G->A->C->E->F->B->H->D->2.1s

Nhìn vào kết quả, tại sao là 2.1s mà không phải là delay() time dài nhất 2.0s. Vì các bạn có thể thấy khoảng time từ khi Main Job bắt đầu đến khi delay của Job1 là 0.01s. Có một số lần chạy, khoảng time đó quá nhỏ nên kết quả có thể là 2s.

2.2. Thread.sleep() on Dispatchers.Main

fun main(args: Array<String>) { runBlocking { run() } }
} suspend fun run() { coroutineScope { val timeInMillis = measureTimeMillis { val mainJob = launch { //Job 0 launch { print("A->") delay(1000) print("B->") } //Job 1 launch { print("C->") Thread.sleep(2000) print("D->") } //Job 2 launch { print("E->") delay(500) print("F->") } //Main job print("G->") delay(1500) print("H->") } mainJob.join() } val timeInSeconds = String.format("%.1f", timeInMillis/1000f) print("${timeInSeconds}s") } } 

Luồng chạy sẽ như nhau:

Main Job sẽ chạy và sẽ bị tạm dừng bởi delay.

Tiếp đó là Job 0 -> Job 1. Job sẽ bị suspended. Tuy nhiên Thread.sleep(2000) chạy ở Job 1, nó sẽ block Main Thread trong 2s. Job2 trong thời gian dó không thực thi gì cả.

Sau 2s, D sễ được in ra, tiếp đó là E của Job2. Job 2 sẽ bị suspend. Vì Main Job và Job 1 có suspend time nhỏ hơn 2s, vậy nên sau đó nó sẽ được in ra ngay lập tức. Lúc này Job 0 sẽ được chạy trước vì có time delay bé hơn.

Cuối cùng là 0.5s, Job2 sẽ tiếp tục chạy và hoàn thành in ra F.

Kết quả là: G->A->C->D->E->B->H->F->2.5s

Timestamp #1 (sau 0 second)

  • Main job và Job 0 start and suspended.

  • Job 1 start và blocks Thread

Timestamp #2 (sau 2 seconds)

  • Job 1 hoàn thành

  • Job 2 start and suspended.

  • Job 0 và Main Job tiếp tục và hoàn thành.

Timestamp #3 (after 0.5 seconds)

  • Job 3 tiếp tục và hoàn thành.

Khoảng thời gian tổng dao động trong 2.5s.

2.3. Thread.sleep() on Dispatchers.Default/IO or Dispatchers.Default/Default

fun main(args: Array<String>) { withContext(Dispatchers.Default) { run() }
} suspend fun run() { coroutineScope { val timeInMillis = measureTimeMillis { val mainJob = launch { //Job 0 launch { print("A->") delay(1000) print("B->") } //Job 1 launch { print("C->") Thread.sleep(2000) print("D->") } //Job 2 launch { print("E->") delay(500) print("F->") } //Main job print("G->") delay(1500) print("H->") } mainJob.join() } val timeInSeconds = String.format("%.1f", timeInMillis/1000f) print("${timeInSeconds}s") } } 

Kết quả là:

G->A->C->E->F->B->H->D->2.0s

Kết quả khá tương tự với ví dụ sử dụng kotlinx.coroutines.delay()

Khi Dispatchers.Default hoặc Dispatchers.IO được sử dụng, nó được hỗ trợ bởi một nhóm luồng. Mỗi lần chúng ta gọi launch{}, một worker thread khác được created/used.

Ví dụ, đây là các Worker thread đang được sử dụng:

Main Job - DefaultDispatcher-worker-1

Job 0 - DefaultDispatcher-worker-2

Job 1 - DefaultDispatcher-worker-3

Job 2 - DefaultDispatcher-worker-4

Để xem luồng nào hiện đang chạy, bạn có thể sử dụng println("Run ${Thread.currentThread().name}")

Vì vậy, Thread.sleep() thực sự chặn luồng đó, nhưng chỉ chặn DefaultDispatcher-worker-3. Các công việc khác vẫn có thể được tiếp tục chạy vì chúng nằm trên các luồng khác nhau.

Timestamp #1 (after 0 second)

  • Main Job, Job 0, Job 1 và Job 2 start.

  • Main Job, Job 0 và Job2 bị suspended.

  • Job 1 bị block trên Thread của nó, ở đây là DefaultDispatcher-worker-3.

Timestamp #2 (after 0.5 second)

  • Job 2 tiếp tục và hoàn thành

Timestamp #3 (after 1 second)

  • Job 0 tiếp tục và hoàn thành

Timestamp #4 (after 1.5 seconds)

  • Main Job tiếp tục và hoàn thành

Timestamp #5 (after 2 seconds)

  • Job 1 tiếp tục và hoàn thành

Bởi vì mỗi công việc chạy trên một luồng khác nhau, công việc có thể được bắt đầu vào những thời điểm khác nhau. Vì vậy, đầu ra của A, C, E, G có thể là ngẫu nhiên. Như vậy, bạn thấy trình tự các task có thể khác với trình tự trong Exampe 1 ở phần trên.

3. Conclusion

Thread.sleep() chặn luồng gọi nó còn kotlinx.coroutines.delay() thì không.

Chỉ nên Thread.sleep() để kiểm tra xem tôi đã đặt đúng tác vụ chạy dài vào chuỗi nền chưa.

Cuối cùng, kotlinx.coroutines.delay() là phương pháp được khuyến nghị để tạo độ trễ trong một coroutine mà không chặn luồng giao diện người dùng.

Cảm ơn các bạn đã theo dõi.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 281

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 214

- vừa được xem lúc

[Android] Hiển thị Activity trên màn hình khóa - Show Activity over lock screen

Xin chào các bạn, Hôm nay là 30 tết rồi, ngồi ngắm trời chờ đón giao thừa, trong lúc rảnh rỗi mình quyết định ngồi viết bài sau 1 thời gian vắng bóng. .

0 0 107

- vừa được xem lúc

Tìm hiểu Proguard trong Android

1. Proguard là gì . Cụ thể nó giúp ứng dụng của chúng ta:. .

0 0 101

- vừa được xem lúc

Làm ứng dụng học toán đơn giản với React Native - Phần 6

Chào các bạn một năm mới an khang thịnh vượng, dồi dào sức khỏe. Lại là mình đây Đây là link app mà các bạn đang theo dõi :3 https://play.google.com/store/apps/details?id=com.

0 0 68

- vừa được xem lúc

20 Plugin hữu ích cho Android Studio

1. CodeGlance. Plugin này sẽ nhúng một minimap vào editor cùng với thanh cuộn cũng khá là lớn. Nó sẽ giúp chúng ta xem trước bộ khung của code và cho phép điều hướng đến đoạn code mà ta mong muốn một cách nhanh chóng.

0 0 315