For Channel
-based producer-consumer approach with consumer-local buffer reuse, see the first example in Pattern for managing thread local storage? - #2 by tkf
This pattern is very handy and performs reasonably well in many situations. But, since the whole purpose of Channel
is to serialize the access, it’s not really the best approach. If your input collection is supported by SplittablesBase.jl, FLoops.jl’s @init
can be useful.
But, if you need a buffer pool that works outside producer-consumer pattern or parallel loop, I see that something like SafeBuffer
in the OP could be handy. One way to simplify the synchronization is:
struct BufferPool{T}
buffers::Vector{T}
condition::Threads.Condition
end
function bufferpool(factory, n::Integer)
n > 0 || error("need at least one buffer; got n = $n")
return BufferPool([factory() for _ in 1:n], Threads.Condition())
end
function withbuffer(f, pool::BufferPool)
b = lock(pool.condition) do
while isempty(pool.buffers)
wait(pool.condition)
end
pop!(pool.buffers)
end
try
f(b)
finally
lock(pool.condition) do
push!(pool.buffers, b)
notify(pool.condition)
end
end
end