Worker Pools in Prolog
Our example demonstrates how to implement a worker pool using threads and message passing in Prolog.
:- use_module(library(threads)).
% Worker predicate
worker(Id, Jobs, Results) :-
thread_get_message(Jobs, Job),
( Job == end_of_jobs
-> true
; format('worker ~w started job ~w~n', [Id, Job]),
sleep(1),
format('worker ~w finished job ~w~n', [Id, Job]),
Result is Job * 2,
thread_send_message(Results, Result),
worker(Id, Jobs, Results)
).
% Main predicate
main :-
NumJobs = 5,
message_queue_create(Jobs),
message_queue_create(Results),
% Start 3 worker threads
forall(between(1, 3, W),
thread_create(worker(W, Jobs, Results), _, [])),
% Send jobs
forall(between(1, NumJobs, J),
thread_send_message(Jobs, J)),
% Send end-of-jobs messages
forall(between(1, 3, _),
thread_send_message(Jobs, end_of_jobs)),
% Collect results
forall(between(1, NumJobs, _),
thread_get_message(Results, _)),
% Clean up
message_queue_destroy(Jobs),
message_queue_destroy(Results).
% Run the main predicate
:- main, halt.
In this Prolog implementation:
We use the
threads
library for concurrent programming.The
worker
predicate represents a worker. It receives jobs from theJobs
queue, processes them (simulated by a 1-second sleep), and sends results to theResults
queue.The
main
predicate sets up the job and result queues, creates worker threads, sends jobs, and collects results.We use
message_queue_create
to create message queues for jobs and results.Worker threads are created using
thread_create
.Jobs are sent to workers using
thread_send_message
.Results are collected using
thread_get_message
.We use
forall
for iteration, which is idiomatic in Prolog.
To run the program, save it as worker_pools.pl
and use:
$ swipl worker_pools.pl
worker 1 started job 1
worker 2 started job 2
worker 3 started job 3
worker 1 finished job 1
worker 1 started job 4
worker 2 finished job 2
worker 2 started job 5
worker 3 finished job 3
worker 1 finished job 4
worker 2 finished job 5
This program demonstrates concurrent execution of jobs by multiple workers in Prolog. Despite doing about 5 seconds of total work, the program completes in about 2 seconds due to the concurrent execution by 3 workers.
Note that Prolog’s approach to concurrency is different from imperative languages. It uses message passing between threads rather than shared memory, which aligns well with Prolog’s logical programming paradigm.