In general, I would use Threads.@spawn instead of Threads.@threads on later Julia versions. It gives the scheduler more flexibility, and can handle things like uneven task length pretty well.
One way to handle array reuse (assuming the arrays have fixed size) would be to spawn a fixed set of workers, and give each worker its own array. The key point is that an array is owned and modified by a single Julia Task, which makes it thread-safe. A rough outline might look like this
# Represents the input needed for a simulation
struct SimulationInput
...
end
channel = Channel{SimulationInput, 32}
function producer(channel)
simulation_input = generate_simulation_input()
put!(channel, simulation_input)
end
function worker(channel, buffer_size)
buffer = zeros(buffer_size)
for simulation_input in channel
run_simulation(simulation_input, buffer)
end
end
Threads.@spawn producer(channel)
workers = [Threads.@spawn worker(1000) for i in 1:8]