RSS Feed

Lisp Project of the Day

simple-tasks

You can support this project by donating at:

Donate using PatreonDonate using Liberapay

Or see the list of project sponsors.

simple-tasksthreads

Documentation😀
Docstrings😀
Tests 🥺
Examples🥺
RepositoryActivity🥺
CI 🥺

This is a @Shinmera's library for task processing. It implements abstraction over multithreading/multiprocessing which operates by means of a runner and a task.

A runner in the simple-task is an object responsible for task scheduling. By default, only a simple queued-runner is implemented. It executes all task in a single thread one by one.

simple-task has good documentation but there is no big example showing the essence of the runner/task concept. Let's fix it!

Next example creates a single thread for the runner and starts separate threads where each thread executes a task in a different way.

First, we need to start the runner:

POFTHEDAY> (defvar *thread*
             (simple-tasks:make-runner-thread *runner*))

;; It is the third in this list:

POFTHEDAY> (bt:all-threads)
(#<SB-THREAD:THREAD "sly-channel-1-mrepl-remote-1" RUNNING {10037F5B93}>
 #<SB-THREAD:THREAD "reader-thread" RUNNING {10026F8103}>
 #<SB-THREAD:THREAD "runner thread" waiting on:
      #<WAITQUEUE task-runner-condition {1003231D63}>
    {100323F833}>
 #<SB-THREAD:THREAD "slynk-indentation-cache-thread" waiting on:
      #<WAITQUEUE  {1002700143}>
    {10026F8233}>
 #<SB-THREAD:THREAD "main thread" RUNNING {1001538543}>
 #<SB-THREAD:THREAD "Slynk Sentinel" waiting on:
      #<WAITQUEUE  {10025300B3}>
    {1002529253}>
 #<SB-THREAD:THREAD "control-thread" waiting on:
      #<WAITQUEUE  {10026F8343}>
    {10026F5D73}>)

Next, we'll start our tasks. Each of them will print the current thread. This way we'll ensure all of them are running in the runner's thread:

POFTHEDAY> (defun print-and-return-current-thread ()
             (let ((name (bt:thread-name (bt:current-thread))))
               (format t "Running in \"~A\" thread.~%"
                       name)
               (values name)))

POFTHEDAY> (defvar *first-task*
             (make-instance
              'simple-tasks:call-task
              :func #'print-and-return-current-thread))

POFTHEDAY> (simple-tasks:status *first-task*)
:CREATED

POFTHEDAY> (simple-tasks:schedule-task *first-task*
                                       *runner*)
Running in "runner thread" thread.

POFTHEDAY> (simple-tasks:status *first-task*)
:COMPLETED

POFTHEDAY> (simple-tasks:return-values *first-task*)
"runner thread"

POFTHEDAY> (defvar *second-task*
             (make-instance
              'simple-tasks:blocking-call-task
              :func #'print-and-return-current-thread))

POFTHEDAY> (simple-tasks:schedule-task *second-task*
                                       *runner*)
Running in "runner thread" thread.

POFTHEDAY> (simple-tasks:return-values *second-task*)
"runner thread"

There are also a few shortcuts:

POFTHEDAY> (simple-tasks:call-as-task #'print-and-return-current-thread
                                      *runner*)
Running in "runner thread" thread.
"runner thread"

;; Or

POFTHEDAY> (simple-tasks:with-body-as-task (*runner*)
             (print-and-return-current-thread))
Running in "runner thread" thread.
"runner thread"

This library can be useful when you are working with some subsystems or external libraries which should be accessed only from the single thread.

For example, RCL (CL interface to the R language) library uses it to interop with R language.

If you are interested in other solutions for multithreading and multiprocessing, look at #poftheday posts grouped by corresponding tags:


Brought to you by 40Ants under Creative Commons License