I’m trying to create a worker who will pick up tasks from a global list and execute them without blocking the repl (i.e. the worker executes in the background). In the following MWE, I’d expect that adding tasks to the worker would return nothing (or possibly printing if the function prints), and calling tasks
would show a decreasing number of tasks until it all tasks are executed. From there, the tasker should wait for a new task to be pushed to the list, and pushing a new task would cause it to be executed by the worker.
So far, either I hit a concurrency violation, the tasks are never executed, or the program blocks the repl.
Here is my MWE:
using Base.Threads
# Global task list, locking mechanism, and condition variable
global tasks = []
global tasks_lock = ReentrantLock()
global new_task_condition = Threads.Condition()
# Function to add tasks to the global list
function add_task(new_task)
lock(tasks_lock) do
push!(tasks, new_task)
end
notify(new_task_condition)
end
# Function to define a worker
function worker()
while true
local task = nothing
lock(tasks_lock) do
while isempty(tasks)
wait(new_task_condition)
end
task = popfirst!(tasks)
end
task()
end
end
# Start a worker in the background
Threads.@spawn worker()
# Add tasks to the queue
add_task(() -> println("Hello, world!"))
add_task(() -> println("Goodbye, world!"))
add_task(() -> println(sum(1:1000)))
add_task(() -> sleep(2); println("Task 2 seconds"))
This snippet gives a concurrency violation.