11.1 Threads

► |  | The Racket Reference

1 | Language Model
2 | Notation for Documentation
3 | Syntactic Forms
4 | Datatypes
5 | Structures
6 | Classes and Objects
7 | Units
8 | Contracts
9 | Pattern Matching
10 | Control Flow
11 | Concurrency and Parallelism
12 | Macros
13 | Input and Output
14 | Reflection and Security
15 | Operating System
16 | Memory Management
17 | Unsafe Operations
18 | Running Racket
| Bibliography
| Index

▼ | 11 | Concurrency and Parallelism

11.1 | Threads
11.2 | Synchronization
11.3 | Thread-Local Storage
11.4 | Futures
11.5 | Places
11.6 | Engines
11.7 | Machine Memory Order

► | 11.1 | Threads

11.1.1 | Creating Threads
11.1.2 | Suspending, Resuming, and Killing Threads
11.1.3 | Synchronizing Thread State
11.1.4 | Thread Mailboxes
11.1.5 | Parallel Thread Pools

On this page:

11.1.1 Creating Threads
thread
thread?
current-thread
thread/ suspend-to-kill
call-in-nested-thread
11.1.2 Suspending, Resuming, and Killing Threads
thread-suspend
thread-resume
kill-thread
break-thread
sleep
thread-running?
thread-dead?
11.1.3 Synchronizing Thread State
thread-wait
thread-dead-evt
thread-resume-evt
thread-suspend-evt
11.1.4 Thread Mailboxes
thread-send
thread-receive
thread-try-receive
thread-receive-evt
thread-rewind-receive
11.1.5 Parallel Thread Pools
parallel-thread-pool?
make-parallel-thread-pool
parallel-thread-pool-close

Racket
top contents ← prev up next →

### 11.1 Threads🔗ℹ

> > > Concurrency and Synchronization in The Racket Guide introduces threads.

See Threads for basic information on the Racket thread model. See also Futures and Places.

When a thread is created, it is placed into the management of the current custodian and added to the current thread group. A thread can have any number of custodian managers added through thread-resume. The allocation made by a thread is accounted to the thread’s custodian managers. See custodian-limit-memory for examples.

A thread that has not terminated can be garbage collected (see Garbage Collection) if it is unreachable and suspended or if it is unreachable and blocked on only unreachable events through functions such as semaphore-wait, semaphore-wait/enable-break, channel-put, channel-get, sync, sync/enable-break, or thread-wait. Beware, however, of a limitation on place-channel blocking; see the caveat in Places.

> > > In GRacket, a handler thread for an eventspace is blocked on an internal semaphore when its event queue is empty. Thus, the handler thread is collectible when the eventspace is unreachable and contains no visible windows or running timers.

A thread can be used as a synchronizable event (see Events). A thread is ready for synchronization when thread-wait would not block; the synchronization result of a thread is the thread itself.

#### 11.1.1 Creating Threads🔗ℹ

> procedure (thread thunk [#:pool pool #:keep keep]) → thread?
> thunk : (-> any)
> pool : (or/c #f 'own parallel-thread-pool?) = #f
> keep : (or/c #f 'results) = #f

Calls thunk with no arguments in a new thread of control. The thread procedure returns immediately with a thread descriptor value. When the invocation of thunk returns, the thread created to invoke thunk terminates.

The resulting thread is a coroutine thread if pool is #f. If pool is 'own, then a new parallel thread pool is created, the thread is added to the pool, and the pool is closed (in the sense of parallel-thread-pool-close) to additional threads. If pool is a parallel thread pool, then the new thread is created in that pool, meaning that it shares processor resources with other threads in the same pool. See Parallel Threads for information about parallel threads and performance. Parallel threads do not run in parallel on the BC variant of Racket or when Racket is built without support for parallelism. For more information, see the description of parallel thread pools.

If keep is 'results, results are recorded with the thread so that they can be reported by thread-wait. Otherwise, then the results of thunk are ignored.

Changed in version 8.18.0.2 of package base: Added the #:pool and #:keep arguments.

> procedure (thread? v) → thread?
> v : any/c

Returns #t if v is a thread descriptor, #f otherwise.

> procedure (current-thread) → thread?

Returns the thread descriptor for the currently executing thread.

> procedure (thread/suspend-to-kill thunk) → thread?
> thunk : (-> any)

Like thread, except that “killing” the thread through kill-thread or custodian-shutdown-all merely suspends the thread instead of terminating it.

> procedure (call-in-nested-thread thunk [cust]) → any
> thunk : (-> any)
> cust : custodian? = (current-custodian)

Creates a nested thread managed by cust to execute thunk. (The nested thread’s current custodian is inherited from the creating thread, independent of the cust argument.) The current thread blocks until thunk returns, and the result of the call-in-nested-thread call is the result returned by thunk.

The nested thread’s exception handler is initialized to a procedure that jumps to the beginning of the thread and transfers the exception to the original thread. The handler thus terminates the nested thread and re-raises the exception in the original thread.

If the thread created by call-in-nested-thread dies before thunk returns, the exn:fail exception is raised in the original thread. If the original thread is killed before thunk returns, a break is queued for the nested thread.

If a break is queued for the original thread (with break-thread) while the nested thread is running, the break is redirected to the nested thread. If a break is already queued on the original thread when the nested thread is created, the break is moved to the nested thread. If a break remains queued on the nested thread when it completes, the break is moved to the original thread.

If the thread created by call-in-nested-thread dies while itself in a call to call-in-nested-thread, the outer call to call-in-nested-thread waits for the innermost nested thread to complete, and any breaks pending on the inner threads are moved to the original thread.

#### 11.1.2 Suspending, Resuming, and Killing Threads🔗ℹ

> procedure (thread-suspend thd) → void?
> thd : thread?

Immediately suspends the execution of thd if it is running. If the thread has terminated or is already suspended, thread-suspend has no effect. The thread remains suspended (i.e., it does not execute) until it is resumed with thread-resume. If the current custodian does not solely manage thd (i.e., some custodian of thd is not the current custodian or a subordinate), the exn:fail:contract exception is raised, and the thread is not suspended.

> procedure (thread-resume thd [benefactor]) → void?
> thd : thread?
> benefactor : (or/c thread? custodian? #f) = #f

Resumes the execution of thd if it is suspended and has at least one custodian (possibly added through benefactor, as described below). If the thread has terminated, or if the thread is already running and benefactor is not supplied, or if the thread has no custodian and benefactor is not supplied, then thread-resume has no effect. Otherwise, if benefactor is supplied, it triggers up to three additional actions:

- If benefactor is a thread, whenever it is resumed from a suspended state in the future, then thd is also resumed. (Resuming thd may trigger the resumption of other threads that were previously attached to thd through thread-resume.)

- New custodians may be added to thd’s set of managers. If benefactor is a thread, then all of the thread’s custodians are added to thd. Otherwise, benefactor is a custodian, and it is added to thd (unless the custodian is already shut down). If thd becomes managed by both a custodian and one or more of its subordinates, the redundant subordinates are removed from thd. If thd is suspended and a custodian is added, then thd is resumed only after the addition.

- If benefactor is a thread, whenever it receives a new managing custodian in the future, then thd also receives the custodian. (Adding custodians to thd may trigger adding the custodians to other threads that were previously attached to thd through thread-resume.)

> procedure (kill-thread thd) → void?
> thd : thread?

Terminates the specified thread immediately, or suspends the thread if thd was created with thread/suspend-to-kill. Terminating the main thread exits the application. If thd has already terminated, kill-thread does nothing. If the current custodian does not solely manage thd (i.e., some custodian of thd is not the current custodian or a subordinate), the exn:fail:contract exception is raised, and the thread is not killed or suspended.

Unless otherwise noted, procedures provided by Racket (and GRacket) are kill-safe and suspend-safe; that is, killing or suspending a thread never interferes with the application of procedures in other threads. For example, if a thread is killed while extracting a character from an input port, the character is either completely consumed or not consumed, and other threads can safely use the port.

> procedure (break-thread thd [kind]) → void?
> thd : thread?
> kind : (or/c #f 'hang-up 'terminate) = #f

Registers a break with the specified thread. The optional kind value indicates the kind of break to register, where #f, 'hang-up, and 'terminate correspond to interrupt, hang-up, and terminate breaks respectively. If breaking is disabled in thd, the break will be ignored until breaks are re-enabled. See Breaks for details.

> procedure (sleep [secs]) → void?
> secs : (>=/c 0) = 0

Causes the current thread to sleep until at least secs seconds have passed after it starts sleeping. A zero value for secs simply acts as a hint to allow other threads to execute. The value of secs can be a non-integer to request a sleep duration to any precision; the precision of the actual sleep time is unspecified.

> procedure (thread-running? thd) → any
> thd : thread?

Returns #t if thd has not terminated and is not suspended, #f otherwise.

> procedure (thread-dead? thd) → any
> thd : thread?

Returns #t if thd has terminated, #f otherwise.

#### 11.1.3 Synchronizing Thread State🔗ℹ

> procedure (thread-wait thd [fail-k]) → any
> thd : thread?
> fail-k : (procedure-arity-includes/c 0) = void

Blocks execution of the current thread until thd has terminated. If the thread’s procedure raised an exception, otherwise aborted to the thread’s initial prompt, or was terminated by being killed, then fail-k is called to produce the result of thread-wait. Otherwise, if the thread records its results (see #:keep in thread), those results are returned, while #<void> is return if the thread does not keep its results.

Note that (thread-wait (current-thread)) deadlocks the current thread, but a break can end the deadlock if breaking is enabled and if the thread is the main thread or otherwise accessible; see Breaks.

Unless thd was created with thread/suspend-to-kill, a (thread-wait thd) may potentially continue even if thd is otherwise inaccessible, because a custodian shut down could terminate the thread. As a result, a thread blocking with thread-wait normally cannot be garbage collected (see Garbage Collection). As a special case, however, (thread-wait thd) blocks without preventing garbage collection of the thread if thd is the current thread, since the thread could only continue if a break escapes from the wait.

Changed in version 8.18.0.2 of package base: Added support for threads with values and the fail-k argument.

> procedure (thread-dead-evt thd) → evt?
> thd : thread?

Returns a synchronizable event (see Events) that is ready for synchronization if and only if thd has terminated. Unlike using thd directly, however, retaining a reference to the event does not prevent thd from being garbage collected (see Garbage Collection). The synchronization result of a thread-dead event is the thread-dead event itself.

A thread waiting on the result of (thread-dead-evt thd) normally cannot itself be garbage collected, unless thd was created with thread/suspend-to-kill, along the same lines as waiting via thread-wait. However, there is no special case for waiting on the result of (thread-dead-evt thd) where thd is the current thread.

For a given thd, thread-dead-evt always returns the same (i.e., eq?) result.

> procedure (thread-resume-evt thd) → evt?
> thd : thread?

Returns a synchronizable event (see Events) that becomes ready for synchronization when thd is running. (If thd has terminated, the event never becomes ready.) If thd runs and is then suspended after a call to thread-resume-evt, the result event remains ready; after each suspend of thd a fresh event is generated to be returned by thread-resume-evt. The result of the event is thd, but if thd is never resumed, then reference to the event does not prevent thd from being garbage collected (see Garbage Collection).

> procedure (thread-suspend-evt thd) → evt?
> thd : thread?

Returns a synchronizable event (see Events) that becomes ready for synchronization when thd is suspended. (If thd has terminated, the event will never unblock.) If thd is suspended and then resumes after a call to thread-suspend-evt, the result event remains ready; each resume of thd creates a fresh event to be returned by thread-suspend-evt. The result of the event is thd, but if thd was created with thread (as opposed to thread/suspend-to-kill) and is never resumed, then reference to the event does not prevent thd from being garbage collected (see Garbage Collection).

If thd was created with thread/suspend-to-kill, then waiting on (thread-suspend-evt thd) prevents garbage collection of the waiting thread in the same way as (thread-dead-evt another-thd) for a another-thd created via thread. Furthermore, since the event result is thd, waiting on (thread-suspend-evt thd) prevents garbage collection of thd.

#### 11.1.4 Thread Mailboxes🔗ℹ

Each thread has a mailbox through which it can receive arbitrary messages. In other words, each thread has a built-in asynchronous channel.

> > > See also Buffered Asynchronous Channels.

> procedure (thread-send thd v [fail-thunk]) → any
> thd : thread?
> v : any/c
> | fail-thunk |  | : |  | (or/c (-> any) #f)
> |  |  | = |  | (lambda () (raise-mismatch-error ....))

Queues v as a message to thd without blocking. If the message is queued, the result is #<void>. If thd stops running—as in thread-running?—before the message is queued, then fail-thunk is called (through a tail call) if it is a procedure to produce the result, or #f is returned if fail-thunk is #f.

> procedure (thread-receive) → any/c

Receives and dequeues a message queued for the current thread, if any. If no message is available, thread-receive blocks until one is available.

> procedure (thread-try-receive) → any/c

Receives and dequeues a message queued for the current thread, if any, or returns #f immediately if no message is available.

> procedure (thread-receive-evt) → evt?

Returns a constant synchronizable event (see Events) that becomes ready for synchronization when the synchronizing thread has a message to receive. The synchronization result of a thread-receive event is the thread-receive event itself.

> procedure (thread-rewind-receive lst) → void?
> lst : list?

Pushes the elements of lst back onto the front of the current thread’s queue. The elements are pushed one by one, so that the first available message is the last element of lst.

#### 11.1.5 Parallel Thread Pools🔗ℹ

> procedure (parallel-thread-pool? v) → thread?
> v : any/c

Returns #t if v is a parallel thread pool, #f otherwise.

Added in version 8.18.0.2 of package base.

> procedure (make-parallel-thread-pool [n]) → parallel-thread-pool?
> n : exact-positive-integer? = (processor-count)

Creates a parallel thread pool that can be used to group a parallel thread with other parallel threads. The threads in a pool can use up to n processors to run. If more than n threads are in the pool, not all of them will run in parallel, but they will still all run concurrently.

The new thread pool is placed into the management of the current custodian. If the custodian is shut down, then the pool is closed in the same way as with parallel-thread-pool-close. Any threads already in the pool can continue to run and use the pool’s resources (unless they are also shut down by the same custodian).

A parallel thread pool cannot run threads in parallel on the BC variant of Racket or when Racket is compiled without support for parallelism. In that case, parallel threads behave the same as coroutine threads. For the CS variant of Racket, the futures-enabled? predicate can be used to detect when parallel threads behave differently from coroutine threads.

Added in version 8.18.0.2 of package base.

> procedure (parallel-thread-pool-close p) → void?
> p : parallel-thread-pool?

Closes a parallel thread pool so that no threads can be added to the pool. Any existing threads in the pool are allowed to continue running, and they continue to share the pool’s processor resources.

When no more threads are running within a closed thread pool, or when no threads are allowed to make progress because the pool’s custodian has been shut down, then processor resources allocated to the pool can be returned to the operating system (i.e., operating-system threads allocated to the pool are terminated).

Added in version 8.18.0.2 of package base.

top contents ← prev up next →