Difference between CoroutineScope and coroutineScope in Kotlin

跟風遠走 提交于 2020-01-24 01:07:29

问题


Can anyone give clarity between functions CoroutineScope() and coroutineScope()?

When I tried to check in source, I found that both of them are functions of CoroutineScope.kt. Additionally, coroutineScope() is suspend function while other one is normal function

Below is documentation I could find :

/**
 * Creates a [CoroutineScope] that wraps the given coroutine [context].
 *
 * If the given [context] does not contain a [Job] element, then a default `Job()` is created.
 * This way, cancellation or failure or any child coroutine in this scope cancels all the other children,
 * just like inside [coroutineScope] block.
 */
@Suppress("FunctionName")
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
    ContextScope(if (context[Job] != null) context else context + Job())

And

/**
 * Creates a [CoroutineScope] and calls the specified suspend block with this scope.
 * The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides
 * the context's [Job].
 *
 * This function is designed for _parallel decomposition_ of work. When any child coroutine in this scope fails,
 * this scope fails and all the rest of the children are cancelled (for a different behavior see [supervisorScope]).
 * This function returns as soon as the given block and all its children coroutines are completed.
 * A usage example of a scope looks like this:
 *
 * ```
 * suspend fun showSomeData() = coroutineScope {
 *
 *   val data = async(Dispatchers.IO) { // <- extension on current scope
 *      ... load some UI data for the Main thread ...
 *   }
 *
 *   withContext(Dispatchers.Main) {
 *     doSomeWork()
 *     val result = data.await()
 *     display(result)
 *   }
 * }
 * ```
 *
 * The scope in this example has the following semantics:
 * 1) `showSomeData` returns as soon as the data is loaded and displayed in the UI.
 * 2) If `doSomeWork` throws an exception, then the `async` task is cancelled and `showSomeData` rethrows that exception.
 * 3) If the outer scope of `showSomeData` is cancelled, both started `async` and `withContext` blocks are cancelled.
 * 4) If the `async` block fails, `withContext` will be cancelled.
 *
 * The method may throw a [CancellationException] if the current job was cancelled externally
 * or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope
 * (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope).
 */
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R =
    suspendCoroutineUninterceptedOrReturn { uCont ->
        val coroutine = ScopeCoroutine(uCont.context, uCont)
        coroutine.startUndispatchedOrReturn(coroutine, block)
    }

I want to get clear difference between them. If anyone can answer when to use which one, it would be helpful.


回答1:


Best difference between CoroutineScope (Capital C version) vs coroutineScope (Smaller c version), I could figure out and which was easily understandable was correlating them with Unstructured vs Structured concurrency

Let me share an example :

class MainActivity extends Activity {
    private Button btn;
    public void onCreate(Bundle b) {
        setContentView(R.layout.activity_main);
        btn = (Button) findViewById(R.id.start_btn);
        btn.setOnClickListener( () -> {
        // Starting a coroutine in Main scope, to download user data, and will print it
        CoroutineScope(Dispatchers.Main).launch {
            int result = downloadUserData()
            Toast.makeText(applicationContext, "Result : " + result, Toast.LENGTH_LONG).show()
        });
    }

    private suspend int downloadUserData() {
        int result = 0;
        // Here, we use CoroutineScope (Capital C version) which will start a new scope and 
        // launch coroutine in new scope Dispatchers.IO, Not In Parent Scope which is Dispatchers.Main
        // Thus, this function would directly return without waiting for loop completion and will return 0
        CoroutineScope(Dispatchers.IO).launch {
            for (int i = 0; i < 2000; i++) {
                kotlinx.coroutines.delay(400);
                result++;
            }
        }

        return result;
    }
}

Output : Result : 0

This is an example of Unstructured Concurrency where it is not guaranteed that child coroutine would complete before returning. Thus, caller/parent coroutine would get wrong value returned by child coroutine. Even, when child coroutine has returned already, child coroutine may be running (in Active state) in the background which may lead to Memory Leaks in certain cases.

Solution :

When we need to communicate between multiple coroutines, we need to make sure Structured Concurrency (Recommended)

This can be done by re-using parent/caller coroutine scope inside child/callee coroutine. This can be achieved by coroutineScope {} (Smaller c) version inside child/callee coroutine.

    private suspend int downloadUserData() {
        int result = 0;
        // By using coroutineScope (Smaller c version) below, we ensure that this coroutine would execute in the
        // parent/caller coroutine's scope, so it would make sure that the for loop would complete
        // before returning from this suspended function. This will return 20000 properly
        coroutineScope {
            for (int i = 0; i < 20000; i++) {
                kotlinx.coroutines.delay(400);
                result++;
            }
        }
        return result;
    }

Output : Result : 20000




回答2:


CoroutineScope() is the method which takes a Context as input and gives you Context with a Job as an object of CoroutineScope interface.

You can use this object to launch a coroutine job as following:

suspend fun doNotDoThis() {
  CoroutineScope(coroutineContext).launch {
      println("I'm confused")
  }
}

While, coroutineScope() takes a block/labmda to execute as a coroutine job:

   fun main() = runBlocking { // this: CoroutineScope
    launch { 
        delay(200L)
        println("Task from runBlocking")
    }

    coroutineScope { // Creates a new coroutine scope
        launch {
            delay(500L) 
            println("Task from nested launch")
        }

        delay(100L)
        println("Task from coroutine scope") // This line will be printed before nested launch
    }

    println("Coroutine scope is over") // This line is not printed until nested launch completes
}

References:

Article-1

Article-2

Kotlin-Docs

I hope this answers your questions.




回答3:


CoroutineScope() is nothing but a factory of CoroutineScope objects, and a CoroutineScope object is nothing but a holder of a CoroutineContext. It has no active role in coroutines, but it's an important part of the infrastructure that makes it easy to do structured concurrency properly. This comes from the fact that all coroutine builders like launch or async are extension functions on CoroutineScope and inherit its context.

You will rarely, if ever, have the need to call CoroutineScope() because usually you either pick up an existing coroutine scope or have one created for you by other convenience functions (like MainScope on Android) or Kotlin internals.

coroutineScope(), on the other hand, is one of the coroutine builders, which means it executes the block you pass it inside a sub-coroutine. It is basically an alias for withContext(this.coroutineContext) and you should primarily use it when you want to launch one or more background coroutines while you continue some work in the foreground, and then join on the background coroutines when completing the block.



来源:https://stackoverflow.com/questions/59368838/difference-between-coroutinescope-and-coroutinescope-in-kotlin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!