Is there a way to run a task in Kotlin for at most a fixed amount of time?
For example something like:
val result: XXX? = runAtMostFor(millis = 1000) { ..TRY to do something }
The goal would be to immediately have the result if the task ends before the 1000 millis, or just a null value and to stop doing the task when the deadline comes
You can use withTimeout or withTimeoutOrNull
withTimeout doesn't work if you are doing blocking operation inside. For example, the following code will print "wrong"
import kotlinx.coroutines.*;
fun main() {
runBlocking {
withTimeout(500) {
Thread.sleep(5000);
println("wrong")
}
}
}
There is no way to run blocking code with timeout. Only way would be to modify the code itself to periodically check if it's time to stop.
Related
I am playing around with Kotlin Coroutines, and I ended up in a situation I do not understand. Let's say that I have two suspend functions:
suspend fun coroutine() {
var num = 0
coroutineScope {
for (i in 1..1000) {
launch {
delay(10)
num += 1
}
}
}
println("coroutine: $num")
}
and:
suspend fun runBlocked() = runBlocking {
var num = 0
for (i in 1..1000) {
launch {
delay(10)
num += 1
}
}
println("Run blocking: $num")
}
Then I call them from main() method:
suspend fun main() {
coroutine()
runBlocked()
}
The coroutine() method prints (as expected) a number that is almost never 1000 (usually between 970 and 999). And I understand why.
What I do not understand is why the runBlocked() function allways prints 0.
coroutine: 998
runBlocked: 0
I tried one more time, this time making a similar function to runBlocked(), with the difference that this time the method is returning a value instead of printing:
suspend fun runBlockedWithReturn(): Int = runBlocking {
var num = 0
for (i in 1..1000) {
launch {
delay(10)
num += 1
}
}
return#runBlocking num
}
And then I called it from the main() method:
suspend fun main() {
val result = runBlockedWithReturn()
println("Run blocking with return: $result")
}
...but the method returned 0.
Why is that? And how do I fix the runBlocked() method to print a number that is close to 1000 instead of 0? What am I missing?
runBlocking must never be called from a coroutine in the first place. Since we are violating this contract by putting it in a suspend function, any explanation we have for why it's behaving the way it is might be different on different platforms or in the future.
Aside from that, blocking code should never be called in a coroutine unless you are in a CoroutineContext that uses a dispatcher that can handle it, like Dispatchers.IO.
That said, the reason this is happening is that coroutineScope suspends until all of its children coroutines finish, and runBlocking doesn't. You're launching coroutines out of runBlocking and then immediately returning, so none of those coroutines have necessarily had a chance to even begin running by the time you return num from the function.
If you wanted to wait for all the coroutines launched inside a runBlocking lambda, you need to join() each of them. You can put them in a list and call joinAll() on it. For example:
(1..1000).map {
launch {
delay(10)
num += 1
}
}.joinAll()
But again, runBlocking should never be called in a coroutine. I'm only describing how you would do it if you were using runBlocking from outside a coroutine for its intended purpose, to bridge between non-coroutine and coroutine code.
You should never call runBlocking from inside a suspend fun.
In any event, you don't wait for the launched coroutines to finish before you return a value from runBlocking, but your use of coroutineScope forces the launched coroutines in that function to complete before you get to the return.
Existing answers focus on what we should not do, but I think they miss the point, so the main reason why we see the difference.
Both coroutineScope() and runBlocking() guarantee that after exiting the code block all coroutines inside already finished running. But for some reason, I don't know if intentional or not, you wrote both cases differently. In coroutine() example you put println() below coroutineScope() block, so it is guaranteed to run after all children. On the other hand, in runBlocked() you put println() inside runBlocking(), so it runs concurrently to children. Just rewrite your runBlocked() in a similar way to coroutine(), so put println() below runBlocking() and you will see 1000, as you expected.
Another difference between both examples is that by default runBlocking() runs using a single thread while coroutineScope() could use many of them. For this reason coroutineScope() produces a random value which is a result of unsafe sharing of a mutable state. runBlocking() is more predictable. It always produces 0 if println() is inside it, because println() runs before any children. Or it always produces 1000 if println() is below runBlocking(), because children are in fact running one at a time, they don't modify the value in parallel.
In my Android application I have code that should run periodically in its own coroutine and should be cancelable.
for this I have the following functions:
startJob(): Initializes the job, sets up invokeOnCompletion() and starts the work loop in the respective scope
private fun startJob() {
if (::myJob.isInitialized && myJob.isActive) {
return
}
myJob= Job()
myJob.invokeOnCompletion {
it?.message.let {
var msg = it
if (msg.isNullOrBlank()) {
msg = "Job stopped. Reason unknown"
}
myJobCompleted(msg)
}
}
CoroutineScope(Dispatchers.IO + myJob).launch {
workloop()
}
}
workloop(): The main work loop. Do some work in a loop with a set delay in each iteration:
private suspend fun workloop() {
while (true) {
// doing some stuff here
delay(setDelayInMilliseconds)
}
}
myJobCompleted: do some finalizing. For now simply log a message for testing.
private fun myJobCompleted(msg: String) {
try {
mainActivityReference.logToGUI(msg)
}
catch (e:Exception){
println("debug: " + e.message)
}
}
Running this and calling myJob.Cancel() will throw the following exception in myJobCompleted():
debug: Only the original thread that created a view hierarchy can touch its views.
I'm curious as to why this code isn't running on the main thread, since startJob() IS called from the main thread?
Furthermore: is there a option similar to using a CancellationTokenSource in c#, where the job is not immediately cancelled, but a cancellation request can be checked each iteration of the while loop?
Immediately breaking off the job, regardless of what it is doing (although it will pretty much always be waiting for the delay on cancellation) doesn't seem like a good idea to me.
It is not the contract of Job.invokeOnCompletion to run on the same thread where Job is created. Moreover, such a contract would be impossible to implement.
You can't expect an arbitrary piece of code to run on an arbitrary thread, just because there was some earlier method invocation on that thread. The ability of the Android main GUI thread to execute code submitted from the outside is special, and involves the existence a top-level event loop.
In the world of coroutines, what controls thread assignment is the coroutine context, while clearly you are outside of any context when creating the job. So the way to fix it is to explicitly launch(Dispatchers.Main) a coroutine from within invokeOnCompletion.
About you question on cancellation, you can use withContext(NonCancellable) to surround the part of code you want to protect from cancellation.
I was trying to update the recycler view content from a background thread in Kotlin. I am not using AsyncTask.
Here is my code, i want to know if there is any better way than this:
In my MainActivity, i have progressThread as a member variable.
var progressThread = Thread()
Then in my method where i want to run the thread first i am defining it...like
progressThread = Thread (
Runnable {
kotlin.run {
try {
while (i <= 100 && !progressThread.isInterrupted) {
Thread.sleep(200)
//Some Logic
runOnUiThread {
//this runs in ui thread
}
i++
}
}catch (e:InterruptedException){
progressThread.interrupt()
}
}
})
after that i am starting it in the same method as
progressThread.start()
and for stopping it, i have a listener to cancel the progress and in the callback of that listener, i have written:
progressThread.interrupt()
Updated
Coroutines are stable now,: https://kotlinlang.org/docs/reference/coroutines-overview.html
Old Answer
Yes, you can do this using doAsync from kotlin anko library that is fairly simple and easy to use.
add following line in module level gradle file:
compile "org.jetbrains.anko:anko-commons:0.10.0"
Code example:
val future = doAsync {
// do your background thread task
result = someTask()
uiThread {
// use result here if you want to update ui
updateUI(result)
}
}
code block written in uiThread will only be executed if your Activity or Fragment is in foreground mode (It is lifecycle aware). So if you are trying to stop thread because you don't want your ui code to execute when Activity is in background, then this is an ideal case for you.
As you can check doAsync returns a Future object so you can cancel the background task, by cancel() function:
future.cancel(true)
pass true if you want to stop the thread even when it has started executing.
If you have more specialised case to handle stopping case then you can do the same thing as in your example.
You can use Kotlin Coroutines also but its in Experimental phase, still you can try it out: https://kotlinlang.org/docs/reference/coroutines.html
When my app starts first time I perform task of importing data from disk into CoreData. I do thins in background thread. Then I switch to main thread and perform load from CoreData.
Problem is that sometimes load from CoreData occurs before import from disk is finished. So I need a way to wait for import to finish and only them perform load from db.
How can I do this in Swift?
My code looks like this:
func firstTimeLaunch() {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)) { [unowned self] in
self.importArticlesListFromDisk()
self.importArticlesFromDisk()
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
self.loadArticlesListFromDb()
self.loadArticlesFromDb()
}
}
}
Perhaps you should try adding a completion handler to importArticlesListFromDisk and importArticlesFromDisk, then loading from the db in the completion block.
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)) { [unowned self] in
self.importArticlesAndArticlesListFromDisk() {
// Completion Handler
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
self.loadArticlesListFromDb()
self.loadArticlesFromDb()
}
}
}
I'd recommend using NSOperations. There is a great talk about this from wwdc15
The sample code is also quite interesting for that purpose.
Essentially, you want to create a concurrent operation for your each of your imports:
let's imagine we override the start function of an operation importing your article list from disk:
override func start {
//long running import operation, even async...
//when done: self.finish() //needs kvo overrides
//finish causes the concurrent operation to terminate
}
A very nice thing you can do with operations, is to set dependencies:
let importArticlesFromDiskOp = ...
let importArticlesFromDBOp = ...
importArticlesFromDBOp.addDependency(importArticlesFromDiskOp)
This way your import from DB would only run after the import from disk is done. I personally use this a LOT.
good luck
R
This a simplification and narrowing to another of my questions: Need help parallel traversing a dag in D
Say you've got some code that you want to parallelize. The problem is, some of the things you need to do have prerequisites. So you have to make sure that those prerequisites are done before you add the new task into the pool. The simple conceptual answer is to add new tasks as their prerequisites finish.
Here I have a little chunk of code that emulates that pattern. The problem is, it throws an exception because pool.finish() gets called before a new task is put on the queue by the worker thread. Is there a way to just wait 'till all threads are idle or something? Or is there another construct that would allow this pattern?
Please note: this is a simplified version of my code to illustrate the problem. I can't just use taskPool.parallel() in a foreach.
import std.stdio;
import std.parallelism;
void simpleWorker(uint depth, uint maxDepth, TaskPool pool){
writeln("Depth is: ",depth);
if (++depth < maxDepth){
pool.put( task!simpleWorker(depth,maxDepth,pool));
}
}
void main(){
auto pool = new TaskPool();
auto t = task!simpleWorker(0,5,pool);
pool.put(t);
pool.finish(true);
if (t.done()){ //rethrows the exception thrown by the thread.
writeln("Done");
}
}
I fixed it: http://dpaste.dzfl.pl/eb9e4cfc
I changed to for loop to:
void cleanNodeSimple(Node node, TaskPool pool){
node.doProcess();
foreach (cli; pool.parallel(node.clients,1)){ // using parallel to make it concurrent
if (cli.canProcess()) {
cleanNodeSimple(cli, pool);
// no explicit task creation (already handled by parallel)
}
}
}