What would be the easiest way to limit parallelism...
# coroutines
u
What would be the easiest way to limit parallelism on IO dispatcher? I.e. without creating a separate Executor thread pool, reusing the IO threads, but imposing a logical limit in a given function?
s
Limiting the size of your thread pool will not help you much
You probably want to limit number of concurrent connections, number of coroutines
I’d use a semaphore
u
Why limiting threadpool woudlnt help?
s
Coroutines are using your thread pool when they are not suspended (running).
You can have much more concurrently opened connections that are waiting for response, than you have threads, because coroutines are suspended while waiting for network
u
Well yes, thats kind of what I want, concretely - limit number of parallel downloads
s
I figured
semaphore is the easiest to me, I’ve seen a suspending semaphore implementation in io.ktor.util.cio.Semaphore
dunno what can be more simple than a semphore
u
I dont see what you mean. You're saying Dispatcher from a custom fixed thread pool executor would not work?
I thought it would always work but downside would be those threads cant be used for anything else but downloads
hence my attempt to impose this logically on top of IO
-- so youre saying, this should not be a dispatcher thing, bu a manual semaphore which would suspend a courinte while semaphore is turned on?
d
There's a new
Semaphore
class.
You can also limit concurrency with channels.
s
you create a semaphore with initial value N (number of concurrent thread). SemDown suspends your Coroutine if more than N coroutines are in critical section.
Producer / Consumer with channels is also an option, just not as simple as a semaphore
Create N consumers (downloading coroutines) and feed them via a channel. your producer will suspend if more than N downloads are in progress
Stop thinking threads, this is not your concurrent unit of work anymore 🙂
☝🏼 3
u
true, but this all sounds like a dispatcher to me? i.e. number of concurrent ops / unbounded should be a a dispatcher responsibility, rather than semaphores mixed with business logic?
or maybe custom Dispatcher, wrapping IO and applying semaphores there?
g
You also can limit global parallelism, set max amount of threads on IO dispatcher using system variables
number of concurrent operations
You cannot limit it with dispatcher, just because it's not how Coroutines work. I think to answer properly on your question you should give more details of what exactly you want to limit and what is your use case
u
@gildor hmm what do you mean not how they work? afaik dispatcher dictates how many parallel coroutines can execute truly, i.e. always equal to the number of threads in the dispatcher
g
No, dispatcher limits how many threads dispatch coroutine execution
u
I think were talking about the definition of "coroutine is executing"
I mean unsuspended
g
But you still can start so many coroutines as you want, launch 1kk coroutines that do not block thread (delay, or do non-blocking io) and they will run in parallel on 1 single thread
u
yes yes, how do you say actually being executed coroutine by a thread?
g
Block thread? :)
So, what is your use case and what you want to achive?
u
okay, so dispatcher how many threads are being blocked at most, right?
g
Yes
u
suspend fun downloader.download()
now I run it on IO = unlimited parallel downloads, which is not good in my case and want to limit it to say 5
so overflowing requests need to be queued and processed when dispatcher is free
g
You don't need IO for this, just use asyncronous http client (just assuming that it's some network operation) And actual parallelization will be limited by http client (for example okhttp had 5 parallel connections to the same host)
u
so I figured if I create a dispatcher from a fixed thread pool, I'd get this for free
No I'd only want to limit file downloads / uploads explicitly, shouldn't leak to other requests, glide, etc
g
Own http client for this downloader with own limit?
If you use Glide you probably use okhttp, right?
u
yea okhttp backs everything
isnt it basically the same? separate dispatcher vs clone of okhttp threads
g
Than no need to have own dispatcher, okhttp already have more powerful internal dispatcher to limit amount of parallel requests, and you can just can create new instance of okhttp client for your downloader with own limit
Own coroutines dispatcher wouldn't help you on this case if you use asyncronous API of okhttp
Solution with okhttp dispatcher instead of coroutines is more powerful and optimal, it knows about amount of connections and hosts
And also allows you limit parallelism without using blocking API of okhttp and in case of coroutines dispatcher you have to use it everywhere to limit parallel requests, but In case of limit on level of http client you can share this client between different parts of your code and even with third party libraries
u
well its going to be uglier I think, because okhttp is not exposed, but hidden behind retrofit, now I'd need a new retrofit service to the same endpoint which is weird but ok
why wouldnt it even work you think? afaik there wouldn't be a thread free to call that retrofit/okhttp call
ooh you mean my thread would become free once hopped of to okhttp thread, hence allowing to call new okhttp calls?
g
Sure
Only if you use blocking Retrofit API, but don't think you do
u
I see
g
If okhttp is not exposed it's even better, you create special Reteofit service instance with required parallelism of okhttp client and can use it with coroutines or any other call adapter
It also can be implementation details of your downloader if you wish, the same way as you want to create own coroutines dispatcher
And I disagree that it's uglier, it's just dependency injection, when downloader knows nothing about parallelism, but http client with required config is injected
u
uglier in that another service is needed for the same api, which doesnt make sense unless you know about the business rule of limiting downloads
g
another service is needed for the same api
I don’t understands what you mean
If you want to make this limit per instance of your downloader, not by Retrofit service, just use kotlinx.coroutines Semaphore, as was suggested above. We don’t have details of your architecture and what exactly you want to limit. how and why
u
ok trhank you
was very helpful