All,
I’m happy to announce a (breaking) API upgrade to ThreadPools.jl to better separate choices around handling background tasks and handling nonuniform tasks. Many thanks to @tkf and @ianshmean and many others who contributed to the ideas.
Overview
ThreadPools.jl is a simple package for thread parallelization that exposes a few macros and functions that mimic Base.Threads.@threads , Base.map , and Base.foreach. These macros/functions handle cases that the built-in functions are not always well-suited for:
- Groups of tasks that the user wants to keep off of the primary thread
- Groups of tasks that are very nonuniform in duration
For the first case, ThreadPools exposes a @bthreads (“background threads”) macro that behaves identically to Threads.@threads , but keeps the primary thread job-free. For the second case, the package exposes a @qthreads (“queued threads”) macro. This macro queues up the jobs before thread assignment, only starting a new job when a job on any previous thread has finished. The background version of the same is the @qbthreads (“queued background threads”) macro. Versions of map and foreach with the same prefixes are also available:
| Foreground (primary allowed) | Background (primary forbidden) | |
|---|---|---|
| Uniform tasks |
|
|
| Nonuniform tasks |
|
|
The package also exposes a lower-level @pspawnat macro that mimics the Base.Threads.@spawn macro, but allows direct thread assignment for users who want to develop their own scheduling.
Logging
There are also logging versions of each of the above:
julia> using Plots
pool = logpforeach(x -> sleep(0.01*x), 1:64);
plot(pool)

pool = logqforeach(x -> sleep(0.01*x), 1:64);
plot(pool)

Note that the above examples demonstrate the two scheduling strategies far better than I can describe. ![]()
Composing
There may be times where the default macros don’t quite do what you want. For those cases, a composable API is exposed. For example, perhaps we want to limit usage to only two threads off of the primary:
julia> pwith(ThreadPools.QueuePool(2,2)) do pool
pforeach(pool, x -> println((x,Threads.threadid())), 1:8)
end;
(2, 2)
(1, 3)
(3, 2)
(4, 3)
(5, 2)
(6, 3)
(7, 2)
(8, 3)
Here, only threads 2 and 3 are used (in this case, on an 8-thread machine).
Anyway, thanks for all the early-adopter inputs, and I hope that some find the package useful. Feel free to submit issues at ThreadPools.jl if you find problems or have requests. Cheers, all!