diff --git a/docs/images/coroutines-and-threads.svg b/docs/images/coroutines-and-threads.svg new file mode 100644 index 0000000000..ed38d9d52b --- /dev/null +++ b/docs/images/coroutines-and-threads.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/parallelism-and-concurrency.svg b/docs/images/parallelism-and-concurrency.svg new file mode 100644 index 0000000000..6e56f96ede --- /dev/null +++ b/docs/images/parallelism-and-concurrency.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/run-icon.png b/docs/images/run-icon.png new file mode 100644 index 0000000000..ebc6d3e91c Binary files /dev/null and b/docs/images/run-icon.png differ diff --git a/docs/topics/coroutines-basics.md b/docs/topics/coroutines-basics.md index 5b467c58bf..ff92c0f472 100644 --- a/docs/topics/coroutines-basics.md +++ b/docs/topics/coroutines-basics.md @@ -1,293 +1,499 @@ - https://github.com/Kotlin/kotlinx.coroutines/edit/master/docs/topics/ [//]: # (title: Coroutines basics) -This section covers basic coroutine concepts. +To create applications that perform multiple tasks at once, a concept known as concurrency, +Kotlin uses coroutines. Coroutines let you write concurrent code in a clear and sequential style. -## Your first coroutine +The most basic building block of coroutines is the suspending function. +It makes it possible to pause a running operation and resume it later without losing the structure of your code. -A _coroutine_ is an instance of a suspendable computation. It is conceptually similar to a thread, in the sense that it -takes a block of code to run that works concurrently with the rest of the code. -However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. +To mark a function as suspendable, use the `suspend` keyword: -Coroutines can be thought of as light-weight threads, but there is a number -of important differences that make their real-life usage very different from threads. +```kotlin +suspend fun greet() { + println("Hello world from a suspending function") +} +``` -Run the following code to get to your first working coroutine: +You can only call a suspending function from another suspending function. +To call suspending functions at the entry point of a Kotlin application, mark the `main()` function as suspendable: ```kotlin -import kotlinx.coroutines.* +suspend fun main() { + showUserInfo() +} -//sampleStart -fun main() = runBlocking { // this: CoroutineScope - launch { // launch a new coroutine and continue - delay(1000L) // non-blocking delay for 1 second (default time unit is ms) - println("World!") // print after delay - } - println("Hello") // main coroutine continues while a previous one is delayed +suspend fun showUserInfo() { + println("Loading user...") + greet() + println("User: John Smith") +} + +suspend fun greet() { + println("Hello world from a suspending function") } -//sampleEnd ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt). -> -{style="note"} +{kotlin-runnable="true"} -You will see the following result: +This example doesn't use concurrency yet, but by marking the functions with the `suspend` keyword, +you allow them to call other suspending functions and run concurrent code inside. -```text -Hello -World! -``` +While the `suspend` keyword is part of the core Kotlin language, most coroutine features +are available through the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines) library. - +## Add the kotlinx.coroutines library to your project -Let's dissect what this code does. +To include the `kotlinx.coroutines` library in your project, add the corresponding dependency configuration based on your build tool. -[launch] is a _coroutine builder_. It launches a new coroutine concurrently with -the rest of the code, which continues to work independently. That's why `Hello` has been printed first. +### Gradle -[delay] is a special _suspending function_. It _suspends_ the coroutine for a specific time. Suspending a coroutine -does not _block_ the underlying thread, but allows other coroutines to run and use the underlying thread for -their code. +Add the following dependency to your `build.gradle(.kts)` file: -[runBlocking] is also a coroutine builder that bridges the non-coroutine world of a regular `fun main()` and -the code with coroutines inside of `runBlocking { ... }` curly braces. This is highlighted in an IDE by -`this: CoroutineScope` hint right after the `runBlocking` opening curly brace. + + -If you remove or forget `runBlocking` in this code, you'll get an error on the [launch] call, since `launch` -is declared only on the [CoroutineScope]: +```kotlin +// build.gradle.kts +repositories { + mavenCentral() +} +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%") +} ``` -Unresolved reference: launch + + + + +```groovy +// build.gradle +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%' +} +``` + + + +### Maven + +Add the following dependency to your `pom.xml` file. + +```xml + + + + org.jetbrains.kotlin + kotlinx-coroutines-core + %coroutinesVersion% + + + ... + ``` -The name of `runBlocking` means that the thread that runs it (in this case — the main thread) gets _blocked_ for -the duration of the call, until all the coroutines inside `runBlocking { ... }` complete their execution. You will -often see `runBlocking` used like that at the very top-level of the application and quite rarely inside the real code, -as threads are expensive resources and blocking them is inefficient and is often not desired. +## Create your first coroutines + +Suspending functions are the basic building blocks for concurrency in Kotlin. +A coroutine is a suspendable computation that can run concurrently with other coroutines and potentially in parallel. -### Structured concurrency +On the JVM and in Kotlin/Native, all concurrent code, such as coroutines, runs on _threads_, which are managed by the operating system. +Coroutines can suspend their execution instead of blocking a thread. +This allows one coroutine to suspend while waiting for a network response and another to run on the same thread, ensuring effective resource utilization. -Coroutines follow a principle of -**structured concurrency** which means that new coroutines can only be launched in a specific [CoroutineScope] -which delimits the lifetime of the coroutine. The above example shows that [runBlocking] establishes the corresponding -scope and that is why the previous example waits until `World!` is printed after a second's delay and only then exits. +![Comparing parallel and concurrent threads](parallelism-and-concurrency.svg){width="700"} -In a real application, you will be launching a lot of coroutines. Structured concurrency ensures that they are not -lost and do not leak. An outer scope cannot complete until all its children coroutines complete. -Structured concurrency also ensures that any errors in the code are properly reported and are never lost. +To create a coroutine in Kotlin, you need the following: -## Extract function refactoring +* A suspending function. +* A coroutine scope in which it can run, such as one available inside the `withContext()` function. +* A coroutine builder like `.launch()` to start it. +* A dispatcher to control which threads it uses. -Let's extract the block of code inside `launch { ... }` into a separate function. When you -perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier. -This is your first _suspending function_. Suspending functions can be used inside coroutines -just like regular functions, but their additional feature is that they can, in turn, -use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine. +> You can display coroutine names next to thread names in the output of your code for additional information. +> To do so, pass the `-Dkotlinx.coroutines.debug` VM option in your build tool or IDE run configuration. +> +> See [Debugging coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/topics/debugging.md) for more information. +> +{style="tip"} + +Let's look at an example that uses multiple coroutines in a multithreaded environment: + +1. Import the `kotlinx.coroutines` library: + + ```kotlin + import kotlinx.coroutines.* + ``` + +2. Mark functions that can pause and resume with the `suspend` keyword: + + ```kotlin + suspend fun greet() { + println("Hello world from a suspending function on thread: ${Thread.currentThread().name}") + } + + suspend fun main() {} + ``` + + > While you can mark the `main()` function as `suspend` in some projects, it may not be possible when integrating with existing code or using a framework. + > In that case, check the framework’s documentation to see if it supports calling suspending functions. + > If not, use [`runBlocking`](#runblocking) to call them by blocking the current thread. + > + {style="note"} + + +3. Use [`withContext(Dispatchers.Default)`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html#) to define a safe entry point for multithreaded concurrent code that runs on a shared thread pool: + + ```kotlin + suspend fun main() = withContext(Dispatchers.Default) { + // Add the coroutine builders here + } + ``` + + > The `withContext()` function is typically used for [context switching](coroutine-context-and-dispatchers.md#jumping-between-threads), but in this example, + > it also defines a safe entry point for concurrent code. + > It uses the [`Dispatchers.Default` dispatcher](#coroutine-dispatchers) to run code on a shared thread pool for multithreaded execution. + > + > The coroutines launched inside the `withContext()` block share the same coroutine scope, which ensures [structured concurrency](#coroutine-scope-and-structured-concurrency). + > + {style="note"} + +4. Use a [coroutine builder function](#coroutine-builder-functions) like [`.launch()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html) to start the coroutine: + + ```kotlin + suspend fun main() = withContext(Dispatchers.Default) { + // Starts a coroutine inside the scope + launch { greet() } + println("This runs concurrently and possibly in parallel with the launched coroutines on thread: ${Thread.currentThread().name}") + } + ``` + +5. Combine these pieces to run multiple coroutines at the same time on a shared pool of threads: + + ```kotlin + // Imports the coroutines library + import kotlinx.coroutines.* + + // Defines a suspending function + suspend fun greet() { + println("Hello from greet() on thread: ${Thread.currentThread().name}") + } + + // Runs all concurrent code on a shared thread pool + suspend fun main() = withContext(Dispatchers.Default) { + launch() { + greet() + } + + // Starts another coroutine + launch() { + println("Another coroutine on thread: ${Thread.currentThread().name}") + } + + println("This runs concurrently and possibly in parallel with the launched coroutines on thread: ${Thread.currentThread().name}") + } + ``` + {kotlin-runnable="true"} + +This example demonstrates simple multithreading with coroutines on a shared thread pool. + +> Try running the example multiple times. +> You may notice that the output order and thread names may change each time you run the program. +> This is because the OS decides when threads run and tasks complete. +> +{style="tip"} + +## Coroutine scope and structured concurrency + +When you run many coroutines in an application, you need a way to manage them as a group. +Kotlin coroutines rely on a principle called _structured concurrency_ to provide this structure. + +This principle connects coroutines into a hierarchy of parent and child tasks that share the same lifecycle. +A parent coroutine waits for its children to complete before finishing. +If the parent coroutine fails or is canceled, all its child coroutines are canceled too. +Keeping coroutines connected this way makes cancellation, error handling, and resource cleanup predictable and safe. + +> A coroutine’s lifecycle is the period from its start until it completes, fails, or is canceled. +> +{style="tip"} + +To maintain structured concurrency, new coroutines can only be launched in a [`CoroutineScope`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/) that defines and manages their lifecycle. + +[Coroutine builder functions](#coroutine-builder-functions) are extension functions on `CoroutineScope`. +When you start a coroutine inside another coroutine, it automatically becomes a child of its parent scope. +The parent coroutine's scope waits for all its children to finish before it completes. + +To create a coroutine scope without launching a new coroutine, use the [`coroutineScope()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html) function: ```kotlin +// Imports the kotlin.time.Duration to express duration in seconds +import kotlin.time.Duration.Companion.seconds + import kotlinx.coroutines.* -//sampleStart -fun main() = runBlocking { // this: CoroutineScope - launch { doWorld() } - println("Hello") -} +suspend fun main() { + println("Starting coroutine scope") + coroutineScope { + launch { + delay(1.seconds) + println("The first coroutine completed") + } + launch { + delay(2.seconds) + println("The second coroutine completed") + } + } -// this is your first suspending function -suspend fun doWorld() { - delay(1000L) - println("World!") + // Runs only after all children in the coroutineScope have completed + println("Coroutine scope completed") } -//sampleEnd ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt). -> -{style="note"} +{kotlin-runnable="true"} - +This example uses the [`delay()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html#) function to show how the coroutine scope waits for its child coroutines to finish. +You can specify the wait time in milliseconds, so `delay(1000L)` suspends the coroutine for one second without blocking the thread. -## Scope builder +> Use [`kotlin.time.Duration`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.time/-duration/) from the Kotlin standard library to express durations like `delay(1.seconds)` instead of using milliseconds. +> +{style="tip"} -In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the -[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete. +## Coroutine builder functions -[runBlocking] and [coroutineScope][_coroutineScope] builders may look similar because they both wait for their body and all its children to complete. -The main difference is that the [runBlocking] method _blocks_ the current thread for waiting, -while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages. -Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function. +Coroutine builder functions create new coroutines inside an existing [coroutine scope](#coroutine-scope-and-structured-concurrency). +They require a `CoroutineScope` to run in, either one that is already available, +or one you create using helper functions such as `coroutineScope()` or [`runBlocking()`](#runblocking). +Each builder defines how the coroutine starts and how you interact with its result. -You can use `coroutineScope` from any suspending function. -For example, you can move the concurrent printing of `Hello` and `World` into a `suspend fun doWorld()` function: +> The `withContext()` function doesn't create a new scope but provides access to the current one. +> +{style="tip"} + +### .launch() + +The [`.launch()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html#) coroutine builder function starts a new coroutine without blocking the rest of the scope. +Use `.launch()` to run a task alongside other work when the result isn't needed or you don't want to wait for it: ```kotlin -import kotlinx.coroutines.* +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds -//sampleStart -fun main() = runBlocking { - doWorld() -} +import kotlinx.coroutines.* -suspend fun doWorld() = coroutineScope { // this: CoroutineScope +suspend fun main() = coroutineScope { + // Starts a coroutine that runs without blocking the scope launch { - delay(1000L) - println("World!") + // Delays to simulate background work + delay(100.milliseconds) + println("Sending notification in background") } - println("Hello") + + // Main coroutine continues while a previous one is delayed + println("Scope continues") } -//sampleEnd ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt). -> -{style="note"} +{kotlin-runnable="true"} + +After running this example, you can see that the `main()` function isn't blocked by `.launch()` and keeps running other code while the coroutine works in the background. -This code also prints: +### .async() -```text -Hello -World! +Use the [`.async()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html) builder function to start an asynchronous computation that runs alongside other code and returns a result you can access later. +The result is wrapped in a [`Deferred`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/) object, which you can access by calling the `.await()` function: + +```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + +import kotlinx.coroutines.* + +suspend fun main() = withContext(Dispatchers.Default) { + // Starts downloading the first page + val firstPage = async { + delay(50.milliseconds) + "First page" + } + + // Starts downloading the second page in parallel + val secondPage = async { + delay(100.milliseconds) + "Second page" + } + + // Awaits both results and compares them + val pagesAreEqual = firstPage.await() == secondPage.await() + println("Pages are equal: $pagesAreEqual") +} ``` +{kotlin-runnable="true"} - +### runBlocking() -## Scope builder and concurrency +The [`runBlocking()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) coroutine builder function creates coroutine scope and blocks the current [thread](#comparing-coroutines-and-jvm-threads) until +the coroutines launched in that scope finish. +Unlike other coroutine builders, `runBlocking()` doesn't use a shared thread pool. -A [coroutineScope][_coroutineScope] builder can be used inside any suspending function to perform multiple concurrent operations. -Let's launch two concurrent coroutines inside a `doWorld` suspending function: +Use `runBlocking()` only when there is no other option to call suspending code from non-suspending code: ```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + import kotlinx.coroutines.* -//sampleStart -// Sequentially executes doWorld followed by "Done" -fun main() = runBlocking { - doWorld() - println("Done") +// A third-party interface we cannot change +interface Repository { + fun readItem(): Int } -// Concurrently executes both sections -suspend fun doWorld() = coroutineScope { // this: CoroutineScope - launch { - delay(2000L) - println("World 2") - } - launch { - delay(1000L) - println("World 1") +object MyRepository : Repository { + override fun readItem(): Int { + // Bridges to a suspending function safely + return runBlocking { + myReadItem() + } } - println("Hello") } -//sampleEnd + +suspend fun myReadItem(): Int { + delay(100.milliseconds) + return 4 +} ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt). + +## Coroutine dispatchers + +A [coroutine dispatcher](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/#) controls which thread or thread pool coroutines use for their execution. +Coroutines aren't always tied to a single thread. +They can pause on one thread and resume on another, depending on the dispatcher. +This lets you run many coroutines at the same time without allocating a separate thread for every coroutine. + +A dispatcher works together with the [coroutine scope](#coroutine-scope-and-structured-concurrency) to define when coroutines run and where they run. +While the coroutine scope controls the coroutine's lifecycle, the dispatcher controls which threads are used for execution. + +> You don't have to specify a dispatcher for every coroutine. +> By default, coroutines inherit the dispatcher from their parent scope. +> You can specify a dispatcher if you need to run a coroutine in a different context. > {style="note"} -Both pieces of code inside `launch { ... }` blocks execute _concurrently_, with -`World 1` printed first, after a second from start, and `World 2` printed next, after two seconds from start. -A [coroutineScope][_coroutineScope] in `doWorld` completes only after both are complete, so `doWorld` returns and -allows `Done` string to be printed only after that: +The `kotlinx.coroutines` library includes different dispatchers for different use cases. +For example, [`Dispatchers.Default`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html) runs coroutines on a shared pool of threads performing work in the background, +separate from the main thread. -```text -Hello -World 1 -World 2 -Done -``` +To specify a dispatcher for a coroutine builder like `.launch()`, pass it as an argument: - +```kotlin +suspend fun runWithDispatcher() = coroutineScope { + launch(Dispatchers.Default) { + println("Running on ${Thread.currentThread().name}") + } +} +``` -## An explicit job +Alternatively, you can wrap multiple coroutines in a `withContext()` block to apply a dispatcher to all of them. -A [launch] coroutine builder returns a [Job] object that is a handle to the launched coroutine and can be -used to wait for its completion explicitly. -For example, you can wait for the completion of the child coroutine and then print the "Done" string: +The following example runs on `Dispatchers.Default`, which is optimized for CPU-intensive operations like data processing: ```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + import kotlinx.coroutines.* -fun main() = runBlocking { -//sampleStart - val job = launch { // launch a new coroutine and keep a reference to its Job - delay(1000L) - println("World!") - } - println("Hello") - job.join() // wait until child coroutine completes - println("Done") -//sampleEnd +suspend fun main() = withContext(Dispatchers.Default) { + println("Running withContext block on ${Thread.currentThread().name}") + + val one = async { + println("First calculation starting on ${Thread.currentThread().name}") + val sum = (1..500_000).sum() + delay(200L) + println("First calculation done on ${Thread.currentThread().name}") + sum + } + + val two = async { + println("Second calculation starting on ${Thread.currentThread().name}") + val sum = (500_001..1_000_000).sum() + println("Second calculation done on ${Thread.currentThread().name}") + sum + } + + // Waits for both calculations and prints the result + println("Combined total: ${one.await() + two.await()}") } ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt). -> -{style="note"} +{kotlin-runnable="true"} -This code produces: +> Even though coroutines can suspend and resume on different threads, +> values written before the coroutine suspends are still available within the same coroutine when it resumes. +> +{style="tip"} -```text -Hello -World! -Done -``` +For more information, see [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md) + +## Comparing coroutines and JVM threads + +While coroutines are suspendable computations that run code concurrently like threads on the JVM, they work differently under the hood. + +A _thread_ is managed by the operating system. Threads can run tasks parallel on multiple CPU cores and represent a standard approach to concurrency on the JVM. +When you create a thread, the operating system allocates memory for its stack and uses the kernel to switch between threads. +This makes threads powerful but also resource-intensive. +Each thread usually needs a few megabytes of memory, and typically the JVM can only handle a few thousand threads at once. - +On the other hand, a coroutine isn't bound to a specific thread. +It can suspend on one thread and resume on another, so many coroutines can share the same thread pool. +When a coroutine suspends, the thread isn't blocked, and it remains free to run other tasks. +This makes coroutines much lighter than threads and allows running hundreds of thousands in one process without exhausting system resources. -## Coroutines are light-weight +![Comparing coroutines and threads](coroutines-and-threads.svg){width="700"} -Coroutines are less resource-intensive than JVM threads. Code that exhausts the -JVM's available memory when using threads can be expressed using coroutines -without hitting resource limits. For example, the following code launches -50,000 distinct coroutines that each waits 5 seconds and then prints a period -('.') while consuming very little memory: +Let's look at an example where 50,000 coroutines each wait five seconds and then print a period (`.`): ```kotlin +// Imports the kotlin.time.Duration to express duration in seconds +import kotlin.time.Duration.Companion.seconds + import kotlinx.coroutines.* -fun main() = runBlocking { - repeat(50_000) { // launch a lot of coroutines +suspend fun main() = coroutineScope { + // Launches 50,000 coroutines that each wait five seconds, then print a period + repeat(50_000) { launch { - delay(5000L) + delay(5.seconds) print(".") } } } ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt). -> -{style="note"} - +Now let's look at the same example using JVM threads: -If you write the same program using threads (remove `runBlocking`, replace -`launch` with `thread`, and replace `delay` with `Thread.sleep`), it will -consume a lot of memory. Depending on your operating system, JDK version, -and its settings, it will either throw an out-of-memory error or start threads slowly -so that there are never too many concurrently running threads. +```kotlin +import kotlin.concurrent.thread + +fun main() { + repeat(50_000) { + thread { + Thread.sleep(5000L) + print(".") + } + } +} +``` - - +Running this version uses much more memory because each thread needs its own memory stack. +Depending on your operating system, JDK version, and settings, +it may either throw an out-of-memory error or slow down thread creation to avoid running too many threads at once. -[launch]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html -[delay]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html -[runBlocking]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html -[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html -[_coroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html -[Job]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html +## What's next - +* Discover more about combining suspending functions in [Composing suspending functions](composing-suspending-functions.md). +* Learn how to cancel coroutines and handle timeouts in [Cancellation and timeouts](cancellation-and-timeouts.md). +* Dive deeper into coroutine execution and thread management in [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md). +* Learn how to return multiple asynchronously computed values in [Asynchronous flows](flow.md)