thread_pool.pl -- Resource bounded thread management
The module library(thread_pool) manages threads in pools. A pool defines properties of its member threads and the maximum number of threads that can coexist in the pool. The call thread_create_in_pool/4 allocates a thread in the pool, just like thread_create/3. If the pool is fully allocated it can be asked to wait or raise an error.
The library has been designed to deal with server applications that receive a variety of requests, such as HTTP servers. Simply starting a thread for each request is a bit too simple minded for such servers:
- Creating many CPU intensive threads often leads to a slow-down rather than a speedup.
- Creating many memory intensive threads may exhaust resources
- Tasks that require little CPU and memory but take long waiting for external resources can run many threads.
Using this library, one can define a pool for each set of tasks with comparable characteristics and create threads in this pool. Unlike the worker-pool model, threads are not started immediately. Depending on the design, both approaches can be attractive.
The library is implemented by means of a manager thread with the fixed
thread id __thread_pool_manager
. All state is maintained in this
manager thread, which receives and processes requests to create and
destroy pools, create threads in a pool and handle messages from
terminated threads. Thread pools are not saved in a saved state and
must therefore be recreated using the initialization/1 directive or
otherwise during startup of the application.
- thread_pool_create(+Pool, +Size, +Options) is det
- Create a pool of threads. A pool of threads is a declaration for
creating threads with shared properties (stack sizes) and a
limited number of threads. Threads are created using
thread_create_in_pool/4. If all threads in the pool are in use,
the behaviour depends on the
wait
option of thread_create_in_pool/4 and thebacklog
option described below. Options are passed to thread_create/3, except for- backlog(+MaxBackLog)
- Maximum number of requests that can be suspended. Default
is
infinite
. Otherwise it must be a non-negative integer. Usingbacklog(0)
will never delay thread creation for this pool.
The pooling mechanism does not interact with the
detached
state of a thread. Threads can be created bothdetached
and normal and must be joined using thread_join/2 if they are not detached. - thread_pool_destroy(+Name) is det
- Destroy the thread pool named Name.
- current_thread_pool(?Name) is nondet
- True if Name refers to a defined thread pool.
- thread_pool_property(?Name, ?Property) is nondet
- True if Property is a property of thread pool Name. Defined
properties are:
- options(Options)
- Thread creation options for this pool
- free(Size)
- Number of free slots on this pool
- size(Size)
- Total number of slots on this pool
- members(ListOfIDs)
- ListOfIDs is the list or threads running in this pool
- running(Running)
- Number of running threads in this pool
- backlog(Size)
- Number of delayed thread creations on this pool
- thread_create_in_pool(+Pool, :Goal, -Id, +Options) is det
- Create a thread in Pool. Options overrule default thread
creation options associated to the pool. In addition, the
following option is defined:
- wait(+Boolean)
- If
true
(default) and the pool is full, wait until a member of the pool completes. Iffalse
, throw a resource_error.
- create_pool_lazily(+Pool) is semidet[private]
- Call the hook create_pool/1 to create the pool lazily.
- pool_manager(-ThreadID) is det[private]
- ThreadID is the thread (alias) identifier of the manager. Starts the manager if it is not running.
- manage_thread_pool(+State)[private]
- update_pool(+Message, +Pool0, -Pool) is det[private]
- Deal with create requests and completion messages on a given
pool. There are two messages:
- create(PoolName, Goal, ForThread, Wait, Id, Options)
- Create a new thread on behalf of ForThread. There are
two cases:
- Free slots: create the thread
- No free slots: error or add to waiting
- exitted(PoolName, Thread)
- A thread completed. If there is a request waiting, create a new one.
- worker_exitted(+PoolName, +WorkerId, :AtExit)
- It is possible that '__thread_pool_manager' no longer exists while closing down the process because the manager was killed before the worker.
- create_pool(+PoolName) is semidet[multifile]
- Hook to create a thread pool lazily. The hook is called if
thread_create_in_pool/4 discovers that the thread pool does not
exist. If the hook succeeds, thread_create_in_pool/4 retries
creating the thread. For example, we can use the following
declaration to create threads in the pool
media
, which holds a maximum of 20 threads.:- multifile thread_pool:create_pool/1. thread_pool:create_pool(media) :- thread_pool_create(media, 20, []).