Chức năng đình chỉ có nghĩa là gì trong Kotlin Coroutine


118

Tôi đang đọc Kotlin Coroutine và biết rằng nó dựa trên suspendchức năng. Nhưng suspendnghĩa là gì?

Quy trình hoặc chức năng bị đình chỉ?

Từ https://kotlinlang.org/docs/reference/coroutines.html

Về cơ bản, coroutines là các tính toán có thể bị treo mà không chặn một chuỗi

Tôi nghe mọi người thường nói "chức năng tạm ngưng". Nhưng tôi nghĩ chính đăng quang bị đình chỉ vì chờ hết chức năng? "pause" thường có nghĩa là "ngừng hoạt động", trong trường hợp này, quy trình đăng ký không hoạt động.

🤔 Có nên nói rằng quy trình đăng quang bị đình chỉ không?

Quy trình đăng quang nào bị đình chỉ?

Từ https://kotlinlang.org/docs/reference/coroutines.html

Để tiếp tục tương tự, await () có thể là một hàm tạm ngưng (do đó cũng có thể gọi từ bên trong khối {} không đồng bộ) sẽ tạm dừng một quy trình đăng quang cho đến khi một số tính toán được thực hiện và trả về kết quả của nó:

async { // Here I call it the outer async coroutine
    ...
    // Here I call computation the inner coroutine
    val result = computation.await()
    ...
}

🤔 Nó nói rằng "tạm dừng một quy trình đăng ký cho đến khi một số tính toán được thực hiện", nhưng quy trình đăng ký giống như một sợi chỉ nhẹ. Vì vậy, nếu quy trình đăng quang bị đình chỉ, thì việc tính toán có thể được thực hiện như thế nào?

Chúng tôi thấy awaitđược gọi là trên computation, vì vậy nó có thể asynctrả về Deferred, có nghĩa là nó có thể bắt đầu một quy trình đăng ký khác

fun computation(): Deferred<Boolean> {
    return async {
        true
    }
}

🤔 Trích dẫn nói rằng đình chỉ một quy trình đăng quang . Nó có nghĩa là suspendquy trình đăng quang bên ngoài asynchay suspendquy trình đăng quang bên trong computation?

Liệu suspendcó nghĩa rằng trong khi bên ngoài asynccoroutine đang chờ ( await) cho khu vực nội computationcoroutine đến cuối, nó (ngoài asynccoroutine) idles (do đó tên đình chỉ) và lợi nhuận chủ đề đến hồ bơi thread, và khi trẻ computationkết thúc coroutine, nó (ngoài asynccoroutine ) thức dậy, lấy một chuỗi khác từ hồ bơi và tiếp tục?

Lý do tôi đề cập đến chuỗi này là vì https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html

Chuỗi được trả về nhóm trong khi quy trình đăng ký đang đợi và khi quá trình chờ hoàn tất, quy trình đăng ký tiếp tục trên một chuỗi miễn phí trong nhóm

Câu trả lời:


113

Các chức năng tạm ngưng là trung tâm của tất cả mọi thứ. Chức năng tạm ngừng chỉ đơn giản là một chức năng có thể bị tạm dừng và tiếp tục lại sau đó. Họ có thể thực hiện một hoạt động đang chạy lâu dài và đợi nó hoàn thành mà không bị chặn.

Cú pháp của hàm tạm ngưng tương tự như cú pháp của hàm thông thường ngoại trừ việc thêm suspendtừ khóa. Nó có thể nhận một tham số và có một kiểu trả về. Tuy nhiên, chức năng tạm ngừng chỉ có thể được gọi bởi một chức năng tạm ngừng khác hoặc trong một quy trình.

suspend fun backgroundTask(param: Int): Int {
     // long running operation
}

Bên dưới mui xe, các hàm tạm ngưng được trình biên dịch chuyển đổi thành một hàm khác mà không có từ khóa đình chỉ, có tham số bổ sung kiểu Continuation<T>. Ví dụ, hàm trên sẽ được trình biên dịch chuyển đổi thành:

fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
   // long running operation
}

Continuation<T> là một giao diện có chứa hai hàm được gọi để tiếp tục lại quy trình đăng quang với một giá trị trả về hoặc với một ngoại lệ nếu đã xảy ra lỗi khi hàm bị tạm ngưng.

interface Continuation<in T> {
   val context: CoroutineContext
   fun resume(value: T)
   fun resumeWithException(exception: Throwable)
}

4
Một bí ẩn khác được tiết lộ! Tuyệt quá!
WindRider

16
Tôi tự hỏi chức năng này thực sự bị tạm dừng như thế nào? Họ luôn nói rằng suspend funcó thể tạm dừng nhưng làm thế nào chính xác?
WindRider

2
@WindRider Nó chỉ có nghĩa là luồng hiện tại bắt đầu thực hiện một số quy trình đăng ký khác và sẽ quay lại quy trình này sau.
Joffrey

2
Tôi đã tìm ra cơ chế "bí ẩn". Nó có thể dễ dàng được công bố với sự trợ giúp của Tools> Kotlin> Bytecode> Decompile btn. Nó cho thấy cách mà cái gọi là "điểm tạm ngưng" được thực hiện - thông qua Tiếp tục, v.v. Bất kỳ ai cũng có thể tự mình kiểm tra.
WindRider

4
@buzaa Đây là bài nói từ năm 2017 của Roman Elizarov giải thích điều đó ở cấp bytecode.
Marko Topolnik

30

Để hiểu chính xác ý nghĩa của việc tạm ngừng quy trình đăng ký, tôi khuyên bạn nên xem qua mã này:

import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

var continuation: Continuation<Int>? = null

fun main() = runBlocking {
    launch(Unconfined) {
        val a = a()
        println("Result is $a")
    }
    10.downTo(0).forEach {
        continuation!!.resume(it)
    }
}

suspend fun a(): Int {
    return b()
}

suspend fun b(): Int {
    while (true) {
        val i = suspendCoroutine<Int> { cont -> continuation = cont }
        if (i == 0) {
            return 0
        }
    }
}

Nhân Unconfinedviên điều phối quy trình khám nghiệm loại bỏ sự kỳ diệu của việc điều động đăng kiểm và cho phép chúng tôi tập trung trực tiếp vào các quy trình điều tra trần.

Mã bên trong launchkhối bắt đầu thực thi ngay trên luồng hiện tại, như một phần của lệnh launchgọi. Điều gì xảy ra như sau:

  1. Đánh giá val a = a()
  2. Điều này liên kết với b(), tiếp cận suspendCoroutine.
  3. Hàm b()thực thi khối được chuyển đến suspendCoroutinevà sau đó trả về một COROUTINE_SUSPENDEDgiá trị đặc biệt . Giá trị này không thể quan sát được thông qua mô hình lập trình Kotlin, nhưng đó là những gì phương pháp Java biên dịch thực hiện.
  4. Hàm a(), nhìn thấy giá trị trả về này, bản thân nó cũng trả về nó.
  5. Các launchkhối cũng làm như vậy và kiểm soát bây giờ trở về đến dòng sau khi launchgọi:10.downTo(0)...

Lưu ý rằng, tại thời điểm này, bạn có tác động tương tự như thể mã bên trong launchkhối và fun mainmã của bạn đang thực thi đồng thời. Nó chỉ xảy ra rằng tất cả điều này đang xảy ra trên một chuỗi gốc duy nhất nên launchkhối bị "treo".

Bây giờ, bên trong forEachmã lặp, chương trình đọc hàm continuationb()hàm đã viết và resumesgiá trị của nó 10. resume()được triển khai theo cách giống như thể suspendCoroutinecuộc gọi được trả về với giá trị mà bạn đã chuyển vào. Vì vậy, bạn đột nhiên thấy mình đang thực thi b(). Giá trị bạn đã chuyển để resume()được gán ivà kiểm tra 0. Nếu nó không phải là 0, while (true)vòng lặp sẽ tiếp tục bên trong b(), lại tiếp cận suspendCoroutine, tại thời điểm đó resume()cuộc gọi của bạn trở lại và bây giờ bạn thực hiện một bước lặp khác trong đó forEach(). Điều này tiếp tục cho đến khi bạn tiếp tục với 0, sau đó printlncâu lệnh chạy và chương trình hoàn tất.

Những phân tích trên sẽ cho bạn trực giác quan trọng là "đình chỉ một coroutine" có nghĩa là trả lại quyền kiểm soát trở lại trong cùng launchgọi (hay tổng quát hơn, coroutine xây dựng ). Nếu một quy trình đăng ký lại tạm ngừng sau khi tiếp tục, thì resume()cuộc gọi sẽ kết thúc và quyền điều khiển trở lại người gọi resume().

Sự hiện diện của nhân viên điều phối quy trình làm cho lý do này trở nên ít rõ ràng hơn vì hầu hết họ ngay lập tức gửi mã của bạn đến một chuỗi khác. Trong trường hợp đó, câu chuyện ở trên xảy ra trong luồng khác và trình điều phối quy trình cũng quản lý continuationđối tượng để đối tượng có thể tiếp tục khi giá trị trả về có sẵn.


19

Trước hết, nguồn tốt nhất để hiểu IMO này là bài nói chuyện "Deep Dive into Coroutines" của Roman Elizarov.

Quy trình hoặc chức năng bị đình chỉ?

Gọi một đình chỉ ing chức năng đình chỉ của các coroutine, có nghĩa là các chủ đề hiện tại có thể bắt đầu thực hiện coroutine khác. Vì vậy, coroutine được cho là bị đình chỉ chứ không phải là chức năng.

Trên thực tế, các trang web gọi chức năng tạm ngưng được gọi là "điểm tạm ngưng" vì lý do này.

Quy trình đăng quang nào bị đình chỉ?

Hãy xem mã của bạn và chia nhỏ những gì sẽ xảy ra:

// 1. this call starts a new coroutine (let's call it C1).
//    If there were code after it, it would be executed concurrently with
//    the body of this async
async {
    ...
    // 2. this is a regular function call
    val deferred = computation()
    // 4. because await() is suspendING, it suspends coroutine C1.
    //    This means that if we had a single thread in our dispatcher, 
    //    it would now be free to go execute C2
    // 7. once C2 completes, C1 is resumed with the result `true` of C2's async
    val result = deferred.await() 
    ...
    // 8. C1 can now keep going in the current thread until it gets 
    //    suspended again (or not)
}

fun computation(): Deferred<Boolean> {
    // 3. this async call starts a second coroutine (C2). Depending on the 
    //    dispatcher you're using, you may have one or more threads.
    // 3.a. If you have multiple threads, the block of this async could be
    //      executed in parallel of C1 in another thread. The control flow 
    //      of the current thread returns to the caller of computation().
    // 3.b. If you have only one thread, the block is sort of "queued" but 
    //      not executed right away, and the control flow returns to the 
    //      caller of computation(). (unless a special dispatcher or 
    //      coroutine start argument is used, but let's keep it simple).
    //    In both cases, we say that this block executes "concurrently"
    //    with C1.
    return async {
        // 5. this may now be executed
        true
        // 6. C2 is now completed, so the thread can go back to executing 
        //    another coroutine (e.g. C1 here)
    }
}

Bên ngoài asyncbắt đầu một quy trình đăng quang. Khi nó gọi computation(), bên trong asyncbắt đầu một chương trình đăng quang thứ hai. Sau đó, lệnh gọi để await()tạm dừng việc thực hiện chương trình đăng quang bên ngoài async , cho đến khi quá trình thực thi chương trình đăng ký bên trong async kết thúc.

Bạn thậm chí có thể thấy điều đó với một luồng duy nhất: luồng sẽ thực hiện phần asyncbắt đầu của bên ngoài , sau đó gọi computation()và đến bên trong async. Tại thời điểm này, phần thân của async bên trong bị bỏ qua và luồng tiếp tục thực hiện phần bên ngoài asynccho đến khi nó đạt tới await(). await()là một "điểm treo", bởi vì awaitlà một chức năng đình chỉ. Điều này có nghĩa là trình điều khiển bên ngoài bị tạm dừng và do đó, luồng bắt đầu thực hiện quy trình bên trong. Khi hoàn thành, nó quay lại để thực hiện phần cuối của bên ngoài async.

Có phải tạm ngưng có nghĩa là trong khi quy trình đăng quang không đồng bộ bên ngoài đang đợi (chờ đợi) để quy trình đăng ký tính toán bên trong kết thúc, thì nó (quy trình đăng quang không đồng bộ bên ngoài) sẽ không hoạt động (do đó có tên là tạm ngưng) và trả về luồng vào nhóm luồng và khi quy trình đăng ký tính toán con kết thúc , nó (quy trình đăng quang không đồng bộ bên ngoài) thức dậy, lấy một chuỗi khác từ nhóm và tiếp tục?

Vâng, chính xác.

Cách thực sự đạt được điều này là biến mọi chức năng tạm ngừng thành một máy trạng thái, trong đó mỗi "trạng thái" tương ứng với một điểm tạm ngừng bên trong chức năng tạm ngừng này. Dưới mui xe, hàm có thể được gọi nhiều lần, với thông tin về điểm tạm ngừng mà nó sẽ bắt đầu thực thi từ đó (bạn thực sự nên xem video tôi đã liên kết để biết thêm thông tin về điều đó).


3
Câu trả lời tuyệt vời, tôi nhớ kiểu giải thích thực sự cơ bản đó khi nói đến điều khoản.
bernardo. 19/02/19

Tại sao điều đó không được triển khai bằng bất kỳ ngôn ngữ nào khác? Hay tôi đang thiếu cái gì đó? Tôi đang suy nghĩ về giải pháp đó rất lâu, rất vui vì Kotlin đã có nó, nhưng không hiểu tại sao TS hoặc Rust lại có thứ gì đó như vậy
PEZO

@PEZO well coroutines đã có từ lâu. Kotlin không phát minh ra chúng, nhưng cú pháp và thư viện khiến chúng tỏa sáng. Go có goroutines, JavaScript và TypeScript có những hứa hẹn. Sự khác biệt duy nhất là chi tiết về cú pháp để sử dụng chúng. Tôi thấy khá khó chịu / phiền toái khi các asyncchức năng của JS được đánh dấu theo cách này nhưng vẫn trả về một Lời hứa.
Joffrey

Xin lỗi, nhận xét của tôi không rõ ràng. Tôi đang đề cập đến từ khóa đình chỉ. Nó không giống như async.
PEZO

Cảm ơn vì đã chỉ đến video của Roman. Vàng nguyên chất.
Tố cáo'IN

8

Tôi nhận thấy rằng cách tốt nhất để hiểu suspendlà tạo ra một sự tương tự giữa thistừ khóa và thuộc coroutineContexttính.

Các hàm Kotlin có thể được khai báo là cục bộ hoặc toàn cục. Các hàm cục bộ có quyền truy cập vào thistừ khóa một cách kỳ diệu trong khi toàn cục thì không.

Các hàm Kotlin có thể được khai báo là suspendhoặc chặn. suspendcác hàm có quyền truy cập vào thuộc coroutineContexttính một cách kỳ diệu trong khi các hàm chặn thì không.

Vấn đề là: thuộc coroutineContexttính được khai báo giống như thuộc tính "bình thường" trong Kotlin stdlib nhưng khai báo này chỉ là sơ khai cho các mục đích tài liệu / điều hướng. Trong thực tế coroutineContextđược xây dựng trong tài sản nội tại đó có nghĩa dưới mui xe biên dịch kỳ diệu biết tài sản này như nó biết từ khóa ngôn ngữ.

thisTừ khóa làm gì cho các hàm cục bộ là thuộc coroutineContexttính làm cho các suspendhàm: nó cấp quyền truy cập vào ngữ cảnh thực thi hiện tại.

Vì vậy, bạn cần suspendcó quyền truy cập vào thuộc coroutineContexttính - ví dụ của ngữ cảnh quy trình hiện đang được thực thi


5

Tôi muốn cung cấp cho bạn một ví dụ đơn giản về khái niệm tiếp diễn. Đây là những gì một chức năng tạm ngừng làm, nó có thể đóng băng / tạm ngừng và sau đó nó tiếp tục / hoạt động trở lại. Đừng nghĩ đến coroutine về số đề và Semaphore. Hãy nghĩ về nó về mặt tiếp tục và thậm chí là móc gọi lại.

Để rõ ràng, một quy trình đăng quang có thể được tạm dừng bằng cách sử dụng một suspendhàm. hãy điều tra điều này:

Trong android, chúng ta có thể làm điều này ví dụ:

var TAG = "myTAG:"
        fun myMethod() { // function A in image
            viewModelScope.launch(Dispatchers.Default) {
                for (i in 10..15) {
                    if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
                        println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
                        freezePleaseIAmDoingHeavyWork()
                    } else
                        println("$TAG $i")
                    }
            }

            //this area is not suspended, you can continue doing work
        }


        suspend fun freezePleaseIAmDoingHeavyWork() { // function B in image
            withContext(Dispatchers.Default) {
                async {
                    //pretend this is a big network call
                    for (i in 1..10) {
                        println("$TAG $i")
                        delay(1_000)//delay pauses coroutine, NOT the thread. use  Thread.sleep if you want to pause a thread. 
                    }
                    println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
                }
            }
        }

Mã trên in như sau:

I: myTAG: my coroutine is frozen but i can carry on to do other things

I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done

I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10

I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume

I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15

hãy tưởng tượng nó hoạt động như thế này:

nhập mô tả hình ảnh ở đây

Vì vậy, chức năng hiện tại mà bạn khởi chạy từ đó không dừng lại, chỉ là một quy trình đăng ký sẽ tạm dừng trong khi nó tiếp tục. Chuỗi không bị tạm dừng bằng cách chạy chức năng tạm ngưng.

Tôi nghĩ rằng trang web này có thể giúp bạn hiểu mọi thứ và là tài liệu tham khảo của tôi.

Hãy làm điều gì đó mát mẻ và đóng băng chức năng tạm ngưng của chúng tôi ở giữa một lần lặp lại. Chúng tôi sẽ tiếp tục nó sauonResume

Lưu trữ một biến được gọi continuationvà chúng tôi sẽ tải nó với đối tượng tiếp tục coroutines cho chúng tôi:

var continuation: CancellableContinuation<String>? = null

suspend fun freezeHere() = suspendCancellableCoroutine<String> {
            continuation = it
        }

 fun unFreeze() {
            continuation?.resume("im resuming") {}
        }

Bây giờ, hãy quay lại chức năng bị treo của chúng tôi và làm cho nó bị đóng băng ở giữa quá trình lặp lại:

 suspend fun freezePleaseIAmDoingHeavyWork() {
        withContext(Dispatchers.Default) {
            async {
                //pretend this is a big network call
                for (i in 1..10) {
                    println("$TAG $i")
                    delay(1_000)
                    if(i == 3)
                        freezeHere() //dead pause, do not go any further
                }
            }
        }
    }

Sau đó, ở một nơi khác như trong onResume (ví dụ):

override fun onResume() {
        super.onResume()
        unFreeze()
    }

Và vòng lặp sẽ tiếp tục. Khá gọn gàng khi biết rằng chúng ta có thể đóng băng chức năng tạm ngưng bất kỳ lúc nào và tiếp tục nó sau một thời gian trôi qua. Bạn cũng có thể xem xét các kênh


4

Vì đã có nhiều câu trả lời hay, tôi muốn đăng một ví dụ đơn giản hơn cho những người khác.

Trường hợp sử dụng runBlocking :

  • myMethod () là suspendhàm
  • runBlocking { }bắt đầu một Quy trình theo cách chặn. Nó tương tự như cách chúng tôi chặn các luồng thông thường với Threadlớp và thông báo các luồng bị chặn sau các sự kiện nhất định.
  • runBlocking { }không chặn dòng điện thực hiện chủ đề, cho đến khi coroutine (cơ thể giữa {}) được hoàn thành

     override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
        runBlocking {
            Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
            myMethod();
        }
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
        for(i in 1..5) {
            Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
        }
    }

Kết quả này là:

I/TAG: Outer code started on Thread : main
D/TAG: Inner code started  on Thread : main making outer code suspend
// ---- main thread blocked here, it will wait until coroutine gets completed ----
D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- main thread resumes as coroutine is completed ----
I/TAG: Outer code resumed on Thread : main

khởi chạy trường hợp sử dụng:

  • launch { } bắt đầu một quy trình đăng ký đồng thời.
  • Điều này có nghĩa là khi chúng ta chỉ định khởi chạy, một quy trình đăng ký bắt đầu thực thi trên workerluồng.
  • Cả workerren và ren ngoài (từ đó chúng tôi gọi là launch { }) đều chạy đồng thời. Trong nội bộ, JVM có thể thực hiện Phân luồng trước
  • Khi chúng ta yêu cầu nhiều tác vụ chạy song song, chúng ta có thể sử dụng điều này. Có scopeschỉ định thời gian tồn tại của quy trình đăng quang. Nếu chúng tôi chỉ định GlobalScope, quy trình đăng ký sẽ hoạt động cho đến khi thời gian tồn tại của ứng dụng kết thúc.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
        GlobalScope.launch(Dispatchers.Default) {
            Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
            myMethod();
        }
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
            for(i in 1..5) {
                Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }

Kết quả này:

10806-10806/com.example.viewmodelapp I/TAG: Outer code started on Thread : main
10806-10806/com.example.viewmodelapp I/TAG: Outer code resumed on Thread : main
// ---- In this example, main had only 2 lines to execute. So, worker thread logs start only after main thread logs complete
// ---- In some cases, where main has more work to do, the worker thread logs get overlap with main thread logs
10806-10858/com.example.viewmodelapp D/TAG: Inner code started  on Thread : DefaultDispatcher-worker-1 making outer code suspend
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-1

trường hợp sử dụng asyncawait :

  • Khi chúng ta có nhiều nhiệm vụ phải làm chúng phụ thuộc vào sự hoàn thành của người khác asyncawaitsẽ giúp ích.
  • Ví dụ, trong đoạn mã dưới đây, có các 2hàm tạm ngưng myMethod () và myMethod2 (). myMethod2()chỉ nên được thực thi sau khi hoàn thành đầy đủ myMethod() OR myMethod2() phụ thuộc vào kết quả của myMethod(), chúng tôi có thể sử dụng asyncawait
  • asyncbắt đầu một quy trình đăng ký song song tương tự như launch. Tuy nhiên, nó cung cấp một cách để đợi một quy trình đăng quang trước khi bắt đầu một quy trình đăng quang khác song song.
  • Đó là cách await(). asynctrả về một thể hiện của Deffered<T>. Tsẽ là Unitmặc định. Khi chúng ta cần phải chờ đợi cho bất kỳ asynchoàn 's, chúng ta cần phải gọi .await()vào Deffered<T>ví dụ về điều đó async. Giống như trong ví dụ dưới đây, chúng tôi đã gọi innerAsync.await()hàm ý rằng việc thực thi sẽ bị tạm ngưng cho đến khi innerAsynchoàn thành. Chúng ta có thể quan sát thấy điều tương tự ở đầu ra. Việc innerAsynchoàn thành đầu tiên sẽ gọi myMethod(). Và sau đó async innerAsync2bắt đầu tiếp theo , cuộc gọimyMethod2()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
         job = GlobalScope.launch(Dispatchers.Default) {
             innerAsync = async {
                 Log.d(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
                 myMethod();
             }
             innerAsync.await()
    
             innerAsync2 = async {
                 Log.w(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
                 myMethod2();
             }
        }
    
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
        }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
            for(i in 1..5) {
                Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }
    
    private suspend fun myMethod2() {
        withContext(Dispatchers.Default) {
            for(i in 1..10) {
                Log.w(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }

Kết quả này là:

11814-11814/? I/TAG: Outer code started on Thread : main
11814-11814/? I/TAG: Outer code resumed on Thread : main
11814-11845/? D/TAG: Inner code started  on Thread : DefaultDispatcher-worker-2 making outer code suspend
11814-11845/? D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- Due to await() call, innerAsync2 will start only after innerAsync gets completed
11814-11848/? W/TAG: Inner code started  on Thread : DefaultDispatcher-worker-4 making outer code suspend
11814-11848/? W/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 6 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 7 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 8 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 9 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 10 on Thread : DefaultDispatcher-worker-4
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.