I'm programming a class and I was wondering which pair of methods makes more sense for describing a process cycle:
start() -> stop()
start() -> end()
start() -> finish()
Basically these methods will be called before and after executing a task.
What I'm asking is in English (specifically in programming - any language -) which pair is more common to see?
Sorry I'm not native speaker so I would like to hear which one people prefer.
If it is not clear enough please let me know to fix it or add more info.
Thank you in advance.
Update:
The intention of the methods is to call for any "user functions" before and after running the task. For the task itself would do nothing special.
It depends.
If calling the method will abort the task or stop it early, call it abort() or stop().
If calling the method will wait until the task finishes, call it waitFor().
If calling the method will perform the final steps of the task, call it finish().
If calling the method will clean up after the task, call it Dispose() or cleanup().
Most languages have a standard name for such a method; use it.
Update: If I understand your situation correctly, I would recommend OnStarted() and OnCompleted()
Thats a fairly contextual question but generally you could write:
start() -> stop()
begin() -> end()
you could use finish() to clean up and close the program but close() is more used.
start() and stop() make most sense to me, but only if the stop() method is actually stopping a task. (First example I think of is Java's Thread class which has start() and stop() methods.)
However, if it's to do some operation after executing the task rather than stopping the task itself, then a better description would be what that method actually does, closeResources(), cleanup() or so on.
Still somewhat dependant on circumstance, but given update 2, peraps something like setup() and teardown(), init() and completed() would be more appropriate?
Highly subjective and important topic, some interesting points are here and here.
start -> stop ... start/stop some activity; sometimes a service provided to other components; e.g. start/stop listening on a socket and processing incoming requests
start -> finish ... start/finish some task, a processing step, or similar; here, the task is completed in some sense i.e. it is not aborted or cancelled; e.g. start iterative model optimization, finish the current iteration and return the result
end goes better with begin than with start (think Pascal language constructs :-) begin -> end is quite interchangeable with start -> finish
Related
I am trying to understand the coroutineScope() suspend function in Kotlin and I'm having a hard time understanding the exact purpose of this function.
As per the kotlinlang docs,
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.
But I feel this behavior can be achieved by launching a child coroutine and calling join on it.
So for example
suspend fun other() {
coroutineScope {
launch { // some task }
async { // some task }
}
}
This can be written as (scope is a reference to the scope created by the parent coroutine)
suspend fun other(scope: CoroutineScope) {
scope.launch {
launch { // some task }
async { // some task }
}.join()
}
Is there any difference between these two approaches since it looks
like they will produce same result and also seem to work in the same fashion?
If not, is coroutineScope merely a way to reduce this
boilerplate code of passing scope from parent coroutine and
calling join on child coroutine?
TLDR
Using CoroutineScope as in the example adds boilerplate code, is more confusing, error-prone and may handle cases like errors and cancellations differently. coroutineScope() is generally preferred in such cases.
Full answer
These two patterns are conceptually different and are used in different cases. Coroutines are all about sequential code and structured concurrency. Sequential means we can write a traditional code that waits in-place, it doesn't use callbacks, etc. and at the same time we don't get a performance hit. Structured concurrency means concurrent tasks have their owners, tasks consists of smaller sub-tasks that are explicit to the framework.
By mixing both above together we get a very easy to use and error-proof concurrency model where in most cases we don't have to launch background jobs and then manage them manually, watch for errors, handle cancellations, etc. We simply fork into sub-tasks and then join them in-place - that's all.
In Kotlin this is represented by suspend functions. Suspend functions are always executed within some context, this context is passed everywhere implicitly and the coroutines framework provides utils to use this context easily. One of the most common patterns is to fork and then join and this is exactly what coroutineScope() does. It creates a scope for launching sub-tasks and we can't leave this scope until all children are successful. We don't have to pass the scope manually, we don't have to join, we don't have to pass errors from children to their siblings and to parent, we don't have to pass cancellations from the parent to children - this is all automatic.
Therefore, suspend functions and coroutineScope() should be the default way of writing concurrent code with coroutines. This approach is easy to write, easy to read and it is error-proof. We can't easily leak a background task, because coroutineScope() won't let us go anywhere. We can't mistakenly ignore errors from background tasks. Etc.
Of course, in some cases we can't use this pattern. Sometimes, we actually would like to only launch a long-running task and return immediately. Sometimes, we don't consider the caller to be the owner of the task. For example, we could have some kind of a service that manages its tasks and we only schedule these tasks, but the service itself owns them. For these cases we can use CoroutineScope.
By using the scope explicitly we can launch tasks in the different context than the current one or from outside of coroutine world. We generally have more control, but at the same time we partially opt-out of the code correctness guarantees I mentioned above. For example, if we forget to invoke join() we can easily leak background tasks or perform operations in unexpected order. Also, in your case if the coroutine invoking other() is cancelled, all launched operations will be still running in the background. For these reasons, we should use CoroutineScope explicitly only if needed.
Common patterns
As a result of all that was said above, when working with coroutines we usually use one of these patterns:
Suspend function - it runs within the caller context and it waits for all its subtasks, it doesn't launch anything in the background.
Function receiving CoroutineScope either as a param or receiver - usually, that means the function wants to do something with the context even after returning (because otherwise it could be simply a suspend function). It either launches some background tasks or stores the context somewhere for a later use.
Regular function that uses its own CoroutineScope to launch tasks. Usually, this is some kind of a service that keeps its custom context.
At least to me, function which is suspend and receives CoroutineScope is pretty confusing, it is not entirely clear what to expect from it. Will it execute the operation in the caller context or in the provided one? Will it wait to finish or only schedule the operation in the background and return immediately? Maybe it will do both: first do some initial processing synchronously (therefore suspend), but also schedule additional task in the background (therefore scope: CoroutineScope)? We don't know this, we have to read the documentation or source code to understand its behavior. Your second example is unnecessary complication over a simple suspend function.
To further make my point consider this example:
data class User(
val firstName: String,
val lastName: String,
) {
fun getFullName(user: User) = ...
}
This example is far from perfect, but the main point is that it is confusing why we have to pass user to getFullName() if we call this function on a user already. We don't know whether it returns a full name of the passed user, the user we invoked the function on or maybe some kind of a mix? If that would be a member function not receiving a User or a static utility function receiving a User, everything would be clear. But a member function receiving a User is simply confusing. This is similar to your second example where we pass the context both implicitly and explicitly and we don't know which one is used and how exactly.
So, I have a simple algorithm that follows a tree structure in this manner:
Each time it moves from one node to the next, it propagates attributes of the previous node to the next node and so on, to simulate the effects that the nodes have on each other.
Sometimes a node may be connected to more than one node.
With my current implementation the algorithm follows each split path to the end before completing the rest of the tree:
This is sub-optimal, as all the other branches have to wait for the algorithm to finish, which is a lot of wasted time, especially if the tree is very large.
Ideally I would want each split to spawn a new thread, so that all the routes will be explored in parallel.
I am currently new to Kotlin's coroutines, so please bear with me if this seems stupid.
Currently, I am thinking of implementing this in the following way using Kotlin's coroutines (Note: This is approximate code):
suspend fun propagate(startFromNode: Node) {
coroutineScope {
while (true) {
//Do propagation
if (split) {
launch {
propagate(splitNode)
}
}
if (atEndOfPath) {
break
}
}
}
}
I am unsure how Kotlin handles a situation where coroutines can also spawn new coroutines.
If one coroutine throws an exception for some reason, will all the other coroutines that originate from this main coroutine scope be canceled, including the coroutines that have been started by other coroutines?
Also, I would like to achieve this using a recursive function if possible, but it doesn't seem like there is an easy way to do that with coroutines.
Thanks.
You can find out more details about this if read about structured concurrency.
But to answer your immediate questions.
Your implementation looks like what I would've written myself (and probably most people). Recursion seems to be the way to go here and is possible as you've done it.
Yes! Every call to propagate will wait for it's children coroutines to finish before returning, so when one of the children throws an exception the parent and siblings are cancelled (exceptionally). coroutineScope would then throw the exception, which mostly cancels the entire coroutine stack.
Intellij has an inspection (i.e. lint check) that tells you functions that return Deferred should be named something ending in Async.
Naming conventions like these make sense to me in dynamically typed languages. But Kotlin has such a nice type-checker and ecosystem of tooling, so why rely on the convention?
Especially because Kotlin coroutines bake structured concurrency in, the function will probably also take a CoroutineScope parameter, which would serve the same visual cue at the call site:
suspend fun doStuff() = coroutineScope {
doStuffAsync(this /* CoroutineScope */).await()
//...
}
As a side note, I understand the inspection's message that you'd rarely want a function that returns a Deferred instead of a suspend function. That's not my question. My question assumes that you know what you're doing and you want a Deferred.
To begin with, a function should almost never return a Deferred that comes from an async block. It is up to the caller to wrap some unit of work into an async while doing other work in the foreground, then await on the async result before returning, and wrap all that code in a coroutineScope.
The intended source of Deferred instances is the adaptation layer between Kotlin coroutines and 3rd-party async APIs. For such calls it does make sense to put Async in the name and even some Java APIs follow this convention. For example, you may have a
fun fetchOrder(id: String): Deferred<Order>
and use it as
val orderCancelled = fetchOrder(orderId).isCancelled
This code is type-safe and type-correct, it doesn't cause any compiler errors. It looks like it's fetching an order from a remote system and then checking the order status (whether it's cancelled), but what it's actually doing is getting a Deferred<Order> and checking whether the Deferred is cancelled. Because your function name is missing Async, this kind of error is hard to spot.
Yes, you can also request the IDE to give you the return type, but it may take a while before you even suspect what's going on.
Deferred doesn't return the actual value but is "a light-weight non-blocking future that represents a promise to provide a result later".
As you cannot see this from method name, "we name such functions with the "...Async" suffix to highlight the fact that they only start asynchronous computation and one needs to use the resulting deferred value to get the result".
So the reader of your code can instantly see that your method is not returning the actual value but additionally has to call await() on it.
To your second point about the suspend function:
Usually it's other way round, the xyzAsync() function calls the suspend xyz() function.
As functions returning Deferred can be called from anywhere, not only from suspend functions.
I've been reading up about concurrency in Kotlin and thought I started to understand it... Then I discovered that async() has been deprecated in 1.3 and I'm back to the start.
Here's what I'd like to do: create a thread (and it does have to be a thread rather than a managed pool, unfortunately), and then be able to execute async blocks on that thread, and return Deferred instances that will let me use .await().
What is the recommended way to do this in Kotlin?
1. Single-threaded coroutine dispatcher
Here's what I'd like to do: create a thread (and it does have to be a thread rather than a managed pool, unfortunately)
Starting a raw thread to handle your coroutines is an option only if you're prepared to dive deep and implement your own coroutine dispatcher for that case. Kotlin offers support for your requirement via a single-threaded executor service wrapped into a dispatcher. Note that this still leaves you with almost complete control over how you start the thread, if you use the overload that takes a thread factory:
val threadPool = Executors.newSingleThreadExecutor {
task -> Thread(task, "my-background-thread")
}.asCoroutineDispatcher()
2. async-await vs. withContext
and then be able to execute async blocks on that thread, and return Deferred instances that will let me use .await().
Make sure you actually need async-await, which means you need it for something else than
val result = async(singleThread) { blockingCal() }.await()
Use async-await only if you need to launch a background task, do some more stuff on the calling thread, and only then await() on it.
Most users new to coroutines latch onto this mechanism due to its familiarity from other languages and use it for plain sequential code like above, but avoiding the pitfall of blocking the UI thread. Kotlin has a "sequential by default" philosophy which means you should instead use
val result = withContext(singleThread) { blockingCall() }
This doesn't launch a new coroutine in the background thread, but transfers the execution of the current coroutine onto it and back when it's done.
3. Deprecated top-level async
Then I discovered that async() has been deprecated in 1.3
Spawning free-running background tasks is a generally unsound practice because it doesn't behave well in the case of errors or even just unusual patterns of execution. Your calling method may return or fail without awaiting on its result, but the background task will go on. If the application repeatedly re-enters the code that spawns the background task, your singleThread executor's queue will grow without bound. All these tasks will run without a purpose because their requestor is long gone.
This is why Kotlin has deprecated top-level coroutine builders and now you must explicitly qualify them with a coroutine scope whose lifetime you must define according to your use case. When the scope's lifetime runs out, it will automatically cancel all the coroutines spawned within it.
On the example of Android this would amount to binding the coroutine scope to the lifetime of an Activity, as explained in the KDoc of CoroutineScope.
Like it's stated with the message, it's deprecated in favor of calling async with an explicit scope like GlobalScope.async {} instead.
This is the actual implementation of the deprecated method as well.
By removing the top level async function, you'll not run into issues with implicit scopes or wrong imports.
Let me recommend this solution: Kotlin coroutines with returned value
It parallelizes tasks into 3 background threads (so called "triplets pool") but it's easy to change it to be single threaded as per your requirement by replacing tripletsPool with backgroundThread as below:
private val backgroundThread = ThreadPoolExecutor(1, 1, 5L, TimeUnit.SECONDS, LinkedBlockingQueue())
I'm starting with coroutines in Android app. I'm rewriting callbacks to suspendCoroutine<> {} and I've got one dillema: when should I just mark the function as suspend, and when should I wrap the call in some builder (launch, async, etc.)?
Is there some best practice, rule of thumb, or something?
You should write a suspend fun for every asynchronous, callback-based API call you're currently making.
You should wrap in withContext(myThreadPool) every synchronous API call you're making.
All Android-friendly APIs that do I/O use the async approach, so for these you'll be writing suspend funs, but for CPU-intensive tasks (such as decoding images) you may need withContext.
Finally, to be able to use either kind of calls, you must create a top-level coroutine with launch(UI).
Keep in mind that the above is really just a rule of thumb. When you factor your code, many times you realize you need, for example, a suspend fun to make a CPU-intensive operation because there's a withContext somewhere on that call path.
Let me also add a warning about a very typical misuse of the coroutine APIs: you almost never need async-await. Use it only for the cases where you want to truly run it "in the background" while you continue to perform other stuff in your current context. In simpler terms, you should never write
val result = async { calculation() }.await()
Instead you should write
val result = withContext(myThreadPool) { calculation() }