hello, everyone. I wrote below code to run my back...
# coroutines
l
hello, everyone. I wrote below code to run my background thread task. I want terminal the thread’s running when the CoroutineScope cancel. Is it a good way to terminal the thread’s running and its loop? And is there a better way to implement it?
Copy code
suspendCancellableCoroutine { continuation ->
    val loop = true
    val thread = thread {
        var result = ""
        while(loop) {
            // do something
            if(...) {
                result = "Bala Bala"
                break
            }
        }
        continuation.resume(result)
    }

    continuation.invokeOnCancellation {
        try {
            loop = false
            thread.interrupt()
        } catch (e: Exception) {
            // do nothing
        }
    }
}
a
Better not use threads inside coroutines, and use a dispatcher instead.
☝️ 1
☝🏼 1
l
@Alexandru Caraus but when i use a dispatcher like below, when i cancel the CoroutineScope, the loop would not terminal, and the coroutine would not throw
CancellationException
Copy code
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
    var result = ""
    while(loop) {
        // do something
        if(...) {
            result = "Bala Bala"
            break
        }
    }
    result
}
j
Could share the code you are using to show the behavior you're describing? What's
loop
here? You should either check
isActive
in the condition, or make sure you're using suspend functions in the loop. Cancellation is cooperative
🫡 1
c
@Liberty Tom Maybe this will help you understand why your code is not cooperative yet: https://medium.com/androiddevelopers/cancellation-in-coroutines-aa6b90163629
👀 1
d
Within the loop you need to check for cancellations, regardless of whether you use threads or dispatchers. The easiest way would be to call
yield
within the loop
👀 1
u
But the question remains, why mix plain threads and coroutines. Why not
Copy code
withContext(yourDispatcher) {
  while (isActive) {
    // do something
    if(...) {
      result = "Bala Bala"
      break
    }
  }
}
yourDispatcher
can be the best fit of 1. Dispatchers.Default to use the default thread pool 2. Dispatchers.~Default~IO.limitedParallelism(1) to use only on thread from the default pool at a time 3. newSingleThreadContext to create your own thread. Updated option 2 to use Dispatchers.IO as that will grab a thread from an unlimited pool (not reducing the available IO threads) and probably avoids context switching to Dispatchers.Default and Dispatchers.IO. See Dispatchers.IO, “Elasticity for limited parallelism” and “Implementation note”
👀 1
Oh, if
do something
should be interrupted on cancellation as in your example (
thread.interrupt()
), you should wrapp it with runInterruptible
Copy code
while (true) {
  runInterruptible(yourDispatcher) {
    // do something
  }
  if(...) {
    result = "Bala Bala"
    break
  }
}
And it is good practice to factor the `runInterruptible`out into a single function:
Copy code
suspend fun doSomething() = runInterruptible(yourDispatcher) {
  // do something
}

while (true) {
  doSomething()
  if(...) {
    result = "Bala Bala"
    break
  }
}