Is there a way to prevent duplicate coroutine calls? - kotlin

I'm fairly new to Kotlin coroutines - but is there a way to tag them and make sure only one of them is running at any given time?
GlobalScope.launch(Dispatchers.IO) {
print("Hello World", timestamp)
}
GlobalScope.launch(Dispatchers.IO) {
print("Hello World", timestamp)
}
// "Hello World" 1000
// "Hello World" 1000
I was hoping for a solution that resembles something like this
GlobalScope.launch(Dispatchers.IO, "tag_a") {
print("Hello World", timestamp)
}
GlobalScope.launch(Dispatchers.IO, "tag_a") {
print("Hello World", timestamp)
}
// "Hello World" 1000

I would suggest to create a nullable variable of type Job and assign a lauсhed job to it:
private var job: Job? = null
job = GlobalScope.launch(Dispatchers.IO, "tag_a") {
print("Hello World", timestamp)
}
When trying to launch another coroutine just check whether this job is finished. So the whole code would look like the following:
private var job: Job? = null
#Synchronized
fun doSomething() {
val currentJob = job
// launch if job is null or completed
if (currentJob == null || currentJob.isCompleted) {
job = GlobalScope.launch(Dispatchers.IO) {
//print("Hello World", timestamp)
}
}
}
Btw, usage of GlobalScope is discouraged, consider creating your own scope.

I keep a reference to the job outside of my coroutine and call job?.join(). Sample:
private var job: Job? = null
coroutineScope.launch {
job?.join()
job = coroutineContext.job
}
Tags are way cleaner in my opninion.

Related

How to read a file in one coroutine and print lines in another coroutine?

I'm trying to get comfortable with Kotlin/coroutines. My current goal is to read a text file in one coroutine, and emit each line through a Channel to be printed in another coroutine. Here's what I have so far:
fun main() = runBlocking {
val ch = Channel<String>()
launch {
for (msg in ch) {
println(msg.length)
}
}
launch {
File("file.txt").forEachLine {
ch.send(it)
}
}
}
Hopefully this shows my intent, but it doesn't compile because you can't call a suspending function (send) from the lambda passed to forEachLine. In Golang everything is modeled synchronously, so I would just run it in a goroutine and send would block, but Kotlin seems to have a lower level concurrency model. What would be the canonical way to accomplish this?
If it's helpful, my final goal is to read JSON events emitted from a subprocess via stdout. I'll have a separate JSON object on each line, and will need to parse and handle each separately.
This is the best I've been able to come up with so far. It seems to work but I feel like there must be a more idiomatic way to accomplish this.
fun main() = runBlocking {
val ch = Channel<String>()
launch {
for (msg in ch) {
println(msg.length)
}
}
launch {
val istream = File("file.txt").inputStream()
val buf = ByteArray(4096)
while (true) {
val n = istream.read(buf)
if (n == -1) {
break
}
val msg = buf.sliceArray(0..n-1).toString(Charsets.UTF_8)
ch.send(msg)
}
ch.close()
}
}
I've been trying the same and based on some ideas I got from https://kotlinlang.org/docs/channels.html#fan-out the following seems to work nicely:
fun main() {
val fileToRead = File("somefile.csv")
runBlocking {
// Producer reading the file
val fileChannel = readFileIntoChannel(fileToRead)
// Consumer writing file lines to stdout
launch { fileChannel.consumeEach { line -> println(line) } }
}
}
fun CoroutineScope.readFileIntoChannel(f: File) = produce<String> {
for (line in f.bufferedReader().lines() ) { send(line) }
}

Coroutines how to wait for the data and then continue process

I'm learning coroutines with kotlin, and I have problem how process can wait until process 1 finished then it continue to process 2, from my sample below I have object Network which access API server using getNews(it's running well and get the data)
I called this getNews from refreshNews using asynch - await, with the purpose it wait for the data then it continue running, but "The program Not Wait", it just running process 2 then process 1 finish, so I cannot capture data from API in refresh news
// process 1 - calling api this running well can get the data see process 2
object Network {
var status : NewsApiStatus = NewsApiStatus.LOADING
private var viewModelJob = Job()
private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
fun getNews(filter: String, page: Int =1) : newsData? {
var allNews : newsData? = null
coroutineScope.launch {
RetrofitClient.instance.getAllNews(filter, page).enqueue(object: Callback<newsData>{
override fun onFailure(call: Call<newsData>, t: Throwable) {
status = NewsApiStatus.ERROR
}
override fun onResponse(
call: Call<newsData>,
response: Response<newsData>
) {
status = NewsApiStatus.DONE
var listResult = response.body()
if (listResult != null) {
if (listResult.data.isNotEmpty()) {
allNews = listResult
Timber.tag(TAG).i( "process 1 total allNews = ${allNews!!.data.size}")
}
}
}
})
}
return(allNews)
}
}
// process 2 - calling process 1 with runBlocking
fun refreshNews() = runBlocking{
val newsData = async {
Network.getNews("")
}
Timber.tag(TAG).i("proses 2 ${newsData.await()?.data?.size}")
// here I want newsData to wait until it has data
}
// this main program that call process 2
class NewsListViewModel(application: Application) : AndroidViewModel(application) {
init {
refreshNews()
}
}
launch returns a reference to the started job. You can use it to wait for the job to finish by calling join():
val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job
// ...
}
runBlocking {
job.join() // wait until child coroutine completes
}
Currently, your getNews() launches a coroutine and immediately returns. allNews isn't initialised at that point yet.
You need to either call job.join() inside getNews() (would make it blocking), or use async inside getNews() and return its result if you want to keep it asynchronous (you'd need to take the result differently from your http client as you won't be able to initialise the variable declared outside).
It's worth to go through the official coroutine docs:
https://kotlinlang.org/docs/reference/coroutines/basics.html
https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html

Kotlin - Coroutines not behaving as expected

This question is linked to one of my previous questions: Kotlin - Coroutines with loops.
So, this is my current implementation:
fun propagate() = runBlocking {
logger.info("Propagating objectives...")
val variablesWithSetObjectives: List<ObjectivePropagationMapping> =
variables.filter { it.variable.objective != Objective.NONE }
variablesWithSetObjectives.forEach { variableWithSetObjective ->
logger.debug("Propagating objective ${variableWithSetObjective.variable.objective} from variable ${variableWithSetObjective.variable.name}")
val job: Job = launch {
propagate(variableWithSetObjective, variableWithSetObjective.variable.objective, this, variableWithSetObjective)
}
job.join()
traversedVariableNames.clear()
}
logger.info("Done")
}
private tailrec fun propagate(currentVariable: ObjectivePropagationMapping, objectiveToPropagate: Objective, coroutineScope: CoroutineScope, startFromVariable: ObjectivePropagationMapping? = null) {
if (traversedVariableNames.contains(currentVariable.variable.name)) {
logger.debug("Detected loopback condition, stopping propagation to prevent loop")
return
}
traversedVariableNames.add(currentVariable.variable.name)
val objectiveToPropagateNext: Objective =
if (startFromVariable != currentVariable) {
logger.debug("Propagating objective $objectiveToPropagate to variable ${currentVariable.variable.name}")
computeNewObjectiveForVariable(currentVariable, objectiveToPropagate)
}
else startFromVariable.variable.objective
logger.debug("Choosing variable to propagate to next")
val variablesToPropagateToNext: List<ObjectivePropagationMapping> =
causalLinks
.filter { it.toVariable.name == currentVariable.variable.name }
.map { causalLink -> variables.first { it.variable.name == causalLink.fromVariable.name } }
if (variablesToPropagateToNext.isEmpty()) {
logger.debug("Detected end of path, stopping propagation...")
return
}
val variableToPropagateToNext: ObjectivePropagationMapping = variablesToPropagateToNext.random()
logger.debug("Chose variable ${variableToPropagateToNext.variable.name} to propagate to next")
if (variablesToPropagateToNext.size > 1) {
logger.debug("Detected split condition")
variablesToPropagateToNext.filter { it != variableToPropagateToNext }.forEach {
logger.debug("Launching child thread for split variable ${it.variable.name}")
coroutineScope.launch {
propagate(it, objectiveToPropagateNext, this)
}
}
}
propagate(variableToPropagateToNext, objectiveToPropagateNext, coroutineScope)
}
I'm currently running the algorithm on the following variable topology (Note that the algorithm follows arrows coming to a variable, but not arrows leaving from a variable):
Currently I am getting the following debug print result: https://pastebin.com/ya2tmc6s.
As you can see, even though I launch coroutines they don't begin executing until the main propagate recursive function has finished exploring a complete path.
I would want the launched coroutines to start executing immediately instead...
Unless otherwise specified, all the coroutines you start within runBlocking will run on the same thread.
If you want to enable multithreading, you can just change that to runBlocking(Dispatchers.Default). I'm just going to assume that all that code is thread-safe.
If you don't really want to enable multithreading, then you really shouldn't care what order the coroutines run in.

Kotlin coroutines progress counter

I'm making thousands of HTTP requests using async/await and would like to have a progress indicator. I've added one in a naive way, but noticed that the counter value never reaches the total when all requests are done. So I've created a simple test and, sure enough, it doesn't work as expected:
fun main(args: Array<String>) {
var i = 0
val range = (1..100000)
range.map {
launch {
++i
}
}
println("$i ${range.count()}")
}
The output is something like this, where the first number always changes:
98800 100000
I'm probably missing some important detail about concurrency/synchronization in JVM/Kotlin, but don't know where to start. Any tips?
UPDATE: I ended up using channels as Marko suggested:
/**
* Asynchronously fetches stats for all symbols and sends a total number of requests
* to the `counter` channel each time a request completes. For example:
*
* val counterActor = actor<Int>(UI) {
* var counter = 0
* for (total in channel) {
* progressLabel.text = "${++counter} / $total"
* }
* }
*/
suspend fun getAssetStatsWithProgress(counter: SendChannel<Int>): Map<String, AssetStats> {
val symbolMap = getSymbols()?.let { it.map { it.symbol to it }.toMap() } ?: emptyMap()
val total = symbolMap.size
return symbolMap.map { async { getAssetStats(it.key) } }
.mapNotNull { it.await().also { counter.send(total) } }
.map { it.symbol to it }
.toMap()
}
The explanation what exactly makes your wrong approach fail is secondary: the primary thing is fixing the approach.
Instead of async-await or launch, for this communication pattern you should instead have an actor to which all the HTTP jobs send their status. This will automatically handle all your concurrency issues.
Here's some sample code, taken from the link you provided in the comment and adapted to your use case. Instead of some third party asking it for the counter value and updating the GUI with it, the actor runs in the UI context and updates the GUI itself:
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.*
import kotlin.system.*
import kotlin.coroutines.experimental.*
object IncCounter
fun counterActor() = actor<IncCounter>(UI) {
var counter = 0
for (msg in channel) {
updateView(++counter)
}
}
fun main(args: Array<String>) = runBlocking {
val counter = counterActor()
massiveRun(CommonPool) {
counter.send(IncCounter)
}
counter.close()
println("View state: $viewState")
}
// Everything below is mock code that supports the example
// code above:
val UI = newSingleThreadContext("UI")
fun updateView(newVal: Int) {
viewState = newVal
}
var viewState = 0
suspend fun massiveRun(context: CoroutineContext, action: suspend () -> Unit) {
val numCoroutines = 1000
val repeatActionCount = 1000
val time = measureTimeMillis {
val jobs = List(numCoroutines) {
launch(context) {
repeat(repeatActionCount) { action() }
}
}
jobs.forEach { it.join() }
}
println("Completed ${numCoroutines * repeatActionCount} actions in $time ms")
}
Running it prints
Completed 1000000 actions in 2189 ms
View state: 1000000
You're losing writes because i++ is not an atomic operation - the value has to be read, incremented, and then written back - and you have multiple threads reading and writing i at the same time. (If you don't provide launch with a context, it uses a threadpool by default.)
You're losing 1 from your count every time two threads read the same value as they will then both write that value plus one.
Synchronizing in some way, for example by using an AtomicInteger solves this:
fun main(args: Array<String>) {
val i = AtomicInteger(0)
val range = (1..100000)
range.map {
launch {
i.incrementAndGet()
}
}
println("$i ${range.count()}") // 100000 100000
}
There's also no guarantee that these background threads will be done with their work by the time you print the result and your program ends - you can test it easily by adding just a very small delay inside launch, a couple milliseconds. With that, it's a good idea to wrap this all in a runBlocking call which will keep the main thread alive and then wait for the coroutines to all finish:
fun main(args: Array<String>) = runBlocking {
val i = AtomicInteger(0)
val range = (1..100000)
val jobs: List<Job> = range.map {
launch {
i.incrementAndGet()
}
}
jobs.forEach { it.join() }
println("$i ${range.count()}") // 100000 100000
}
Have you read Coroutines basics? There's exact same problem as yours:
val c = AtomicInteger()
for (i in 1..1_000_000)
launch {
c.addAndGet(i)
}
println(c.get())
This example completes in less than a second for me, but it prints some arbitrary number, because some coroutines don't finish before main() prints the result.
Because launch is not blocking, there's no guarantee all of coroutines will finish before println. You need to use async, store the Deferred objects and await for them to finish.

Is there even a simpler way to express anonymous classes in Kotlin?

I translated this Java
new Thread("Cute Thread") {
public void run() {
int a = 3;
}
}.start();
to this Kotlin
object : Thread("Cute Thread") {
override fun run() {
val a = 3
}
}.start()
But I feel that there is a simpler way of doing this, however I can't find any examples.
I've tried
Thread("Cute Thread") { val a = 3 }.start()
But with no success...
PS. I know that starting a Thread like this is a bad practice.
There's no different way to implement an anonymous class (except SAM conversions).
But you can still simplify your code by:
Thread({ val a = 3 }).start()
or with thread name:
Thread({ val a = 3 }, "Cute Thread").start()
One issue here is that the Thread class constructor has parameters in a bad order for Kotlin. For Runnable you can easily use a SAM conversion (a single method interface can be treated as a lambda) but because the lambda is not the last parameter it looks kinda clunky. In the case where there is only one parameter it is fine:
Thread { val a = 3 }.start()
However with more than one parameter, they are backwards causing this uglier syntax with the lambda as a parameter inside the parenthesis:
Thread({ val a = 3 }, "some name").start()
Instead you should use the Kotlin stdlib function thread()
With that function you have simpler syntax of:
// create thread, auto start it, runs lambda in thread
thread { val a = 3 }
// create thread with given name, auto start it, runs lambda in thread
thread(name = "Cute Thread") { val a = 3 }
// creates thread that is paused, with a lambda to run later
thread(false) { val a = 3 }
// creates thread that is paused with a given name, with a lambda to run later
thread(false, name = "Cute Thread") { val a = 3 }
See also: thread() function documentation
You code is absolutely correct. The only way to simplify it is it extract the logic into a function and then reuse it:
fun newThread(name: String, block: () -> Unit): Thread {
return object : Thread(name) {
override fun run() = block()
}.start()
}
Usage:
newThread("Cute Thread") {
val a = 3
}
If you want to extend/implement some class/interface in your anonymous class there is no other way than:
object: Thread("Name"){
//...
}.start()
The simplest possible construction is of course:
val adhoc = object{
// some code here
}
but there are no simpler way to do this.
Documentation, but You probably read that.