Multi-Threading · The Julia Language

Multi-Threading

# Multi-Threading

This experimental interface supports Julia's multi-threading capabilities. Types and functions described here might (and likely will) change in the future.

`Base.Threads.threadid` — Function.

    Threads.threadid()

Get the ID number of the current thread of execution. The master thread has ID `1`.

source
`Base.Threads.nthreads` — Function.

    Threads.nthreads()

Get the number of threads available to the Julia process. This is the inclusive upper bound on `threadid()`.

source
`Base.Threads.@threads` — Macro.

    Threads.@threads

A macro to parallelize a for-loop to run with multiple threads. This spawns `nthreads()` number of threads, splits the iteration space amongst them, and iterates in parallel. A barrier is placed at the end of the loop which waits for all the threads to finish execution, and the loop returns.

source
`Base.Threads.Atomic` — Type.

    Threads.Atomic{T}

Holds a reference to an object of type `T`, ensuring that it is only accessed atomically, i.e. in a thread-safe manner.

Only certain "simple" types can be used atomically, namely the primitive boolean, integer, and float-point types. These are `Bool`, `Int8`...`Int128`, `UInt8`...`UInt128`, and `Float16`...`Float64`.

New atomic objects can be created from a non-atomic values; if none is specified, the atomic object is initialized with zero.

Atomic objects can be accessed using the `[]` notation:

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> x[] = 1
    1

    julia> x[]
    1

Atomic operations use an `atomic_` prefix, such as `atomic_add!`, `atomic_xchg!`, etc.

source
`Base.Threads.atomic_cas!` — Function.

    Threads.atomic_cas!(x::Atomic{T}, cmp::T, newval::T) where T

Atomically compare-and-set `x`

Atomically compares the value in `x` with `cmp`. If equal, write `newval` to `x`. Otherwise, leaves `x` unmodified. Returns the old value in `x`. By comparing the returned value to `cmp` (via `===`) one knows whether `x` was modified and now holds the new value `newval`.

For further details, see LLVM's `cmpxchg` instruction.

This function can be used to implement transactional semantics. Before the transaction, one records the value in `x`. After the transaction, the new value is stored only if `x` has not been modified in the mean time.

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_cas!(x, 4, 2);

    julia> x
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_cas!(x, 3, 2);

    julia> x
    Base.Threads.Atomic{Int64}(2)

source
`Base.Threads.atomic_xchg!` — Function.

    Threads.atomic_xchg!(x::Atomic{T}, newval::T) where T

Atomically exchange the value in `x`

Atomically exchanges the value in `x` with `newval`. Returns the old value.

For further details, see LLVM's `atomicrmw xchg` instruction.

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_xchg!(x, 2)
    3

    julia> x[]
    2

source
`Base.Threads.atomic_add!` — Function.

    Threads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes

Atomically add `val` to `x`

Performs `x[] += val` atomically. Returns the old value. Not defined for `Atomic{Bool}`.

For further details, see LLVM's `atomicrmw add` instruction.

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_add!(x, 2)
    3

    julia> x[]
    5

source
`Base.Threads.atomic_sub!` — Function.

    Threads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes

Atomically subtract `val` from `x`

Performs `x[] -= val` atomically. Returns the old value. Not defined for `Atomic{Bool}`.

For further details, see LLVM's `atomicrmw sub` instruction.

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_sub!(x, 2)
    3

    julia> x[]
    1

source
`Base.Threads.atomic_and!` — Function.

    Threads.atomic_and!(x::Atomic{T}, val::T) where T

Atomically bitwise-and `x` with `val`

Performs `x[] &= val` atomically. Returns the old value.

For further details, see LLVM's `atomicrmw and` instruction.

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_and!(x, 2)
    3

    julia> x[]
    2

source
`Base.Threads.atomic_nand!` — Function.

    Threads.atomic_nand!(x::Atomic{T}, val::T) where T

Atomically bitwise-nand (not-and) `x` with `val`

Performs `x[] = ~(x[] & val)` atomically. Returns the old value.

For further details, see LLVM's `atomicrmw nand` instruction.

Examples

    julia> x = Threads.Atomic{Int}(3)
    Base.Threads.Atomic{Int64}(3)

    julia> Threads.atomic_nand!(x, 2)
    3

    julia> x[]
    -3

source
`Base.Threads.atomic_or!` — Function.

    Threads.atomic_or!(x::Atomic{T}, val::T) where T

Atomically bitwise-or `x` with `val`

Performs `x[] |= val` atomically. Returns the old value.

For further details, see LLVM's `atomicrmw or` instruction.

Examples

    julia> x = Threads.Atomic{Int}(5)
    Base.Threads.Atomic{Int64}(5)

    julia> Threads.atomic_or!(x, 7)
    5

    julia> x[]
    7

source
`Base.Threads.atomic_xor!` — Function.

    Threads.atomic_xor!(x::Atomic{T}, val::T) where T

Atomically bitwise-xor (exclusive-or) `x` with `val`

Performs `x[] $= val` atomically. Returns the old value.

For further details, see LLVM's `atomicrmw xor` instruction.

Examples

    julia> x = Threads.Atomic{Int}(5)
    Base.Threads.Atomic{Int64}(5)

    julia> Threads.atomic_xor!(x, 7)
    5

    julia> x[]
    2

source
`Base.Threads.atomic_max!` — Function.

    Threads.atomic_max!(x::Atomic{T}, val::T) where T

Atomically store the maximum of `x` and `val` in `x`

Performs `x[] = max(x[], val)` atomically. Returns the old value.

For further details, see LLVM's `atomicrmw max` instruction.

Examples

    julia> x = Threads.Atomic{Int}(5)
    Base.Threads.Atomic{Int64}(5)

    julia> Threads.atomic_max!(x, 7)
    5

    julia> x[]
    7

source
`Base.Threads.atomic_min!` — Function.

    Threads.atomic_min!(x::Atomic{T}, val::T) where T

Atomically store the minimum of `x` and `val` in `x`

Performs `x[] = min(x[], val)` atomically. Returns the old value.

For further details, see LLVM's `atomicrmw min` instruction.

Examples

    julia> x = Threads.Atomic{Int}(7)
    Base.Threads.Atomic{Int64}(7)

    julia> Threads.atomic_min!(x, 5)
    7

    julia> x[]
    5

source
`Base.Threads.atomic_fence` — Function.

    Threads.atomic_fence()

Insert a sequential-consistency memory fence

Inserts a memory fence with sequentially-consistent ordering semantics. There are algorithms where this is needed, i.e. where an acquire/release ordering is insufficient.

This is likely a very expensive operation. Given that all other atomic operations in Julia already have acquire/release semantics, explicit fences should not be necessary in most cases.

For further details, see LLVM's `fence` instruction.

source

## ccall using a threadpool (Experimental)

`Base.@threadcall` — Macro.

    @threadcall((cfunc, clib), rettype, (argtypes...), argvals...)

The `@threadcall` macro is called in the same way as `ccall` but does the work in a different thread. This is useful when you want to call a blocking C function without causing the main `julia` thread to become blocked. Concurrency is limited by size of the libuv thread pool, which defaults to 4 threads but can be increased by setting the `UV_THREADPOOL_SIZE` environment variable and restarting the `julia` process.

Note that the called function should never call back into Julia.

source

## Synchronization Primitives

`Base.Threads.AbstractLock` — Type.

    AbstractLock

Abstract supertype describing types that implement the thread-safe synchronization primitives: `lock`, `trylock`, `unlock`, and `islocked`.

source
`Base.lock` — Function.

    lock(lock)

Acquire the `lock` when it becomes available. If the lock is already locked by a different task/thread, wait for it to become available.

Each `lock` must be matched by an `unlock`.

source
`Base.unlock` — Function.

    unlock(lock)

Releases ownership of the `lock`.

If this is a recursive lock which has been acquired before, decrement an internal counter and return immediately.

source
`Base.trylock` — Function.

    trylock(lock) -> Success (Boolean)

Acquire the lock if it is available, and return `true` if successful. If the lock is already locked by a different task/thread, return `false`.

Each successful `trylock` must be matched by an `unlock`.

source
`Base.islocked` — Function.

    islocked(lock) -> Status (Boolean)

Check whether the `lock` is held by any task/thread. This should not be used for synchronization (see instead `trylock`).

source
`Base.ReentrantLock` — Type.

    ReentrantLock()

Creates a reentrant lock for synchronizing `Task`s. The same task can acquire the lock as many times as required. Each `lock` must be matched with an `unlock`.

This lock is NOT threadsafe. See `Threads.Mutex` for a threadsafe lock.

source
`Base.Threads.Mutex` — Type.

    Mutex()

These are standard system mutexes for locking critical sections of logic.

On Windows, this is a critical section object, on pthreads, this is a `pthread_mutex_t`.

See also `SpinLock` for a lighter-weight lock.

source
`Base.Threads.SpinLock` — Type.

    SpinLock()

Create a non-reentrant lock. Recursive use will result in a deadlock. Each `lock` must be matched with an `unlock`.

Test-and-test-and-set spin locks are quickest up to about 30ish contending threads. If you have more contention than that, perhaps a lock is the wrong way to synchronize.

See also `RecursiveSpinLock` for a version that permits recursion.

See also `Mutex` for a more efficient version on one core or if the lock may be held for a considerable length of time.

source
`Base.Threads.RecursiveSpinLock` — Type.

    RecursiveSpinLock()

Creates a reentrant lock. The same thread can acquire the lock as many times as required. Each `lock` must be matched with an `unlock`.

See also `SpinLock` for a slightly faster version.

See also `Mutex` for a more efficient version on one core or if the lock may be held for a considerable length of time.

source
`Base.Semaphore` — Type.

    Semaphore(sem_size)

Create a counting semaphore that allows at most `sem_size` acquires to be in use at any time. Each acquire must be matched with a release.

This construct is NOT threadsafe.

source
`Base.acquire` — Function.

    acquire(s::Semaphore)

Wait for one of the `sem_size` permits to be available, blocking until one can be acquired.

source
`Base.release` — Function.

    release(s::Semaphore)

Return one permit to the pool, possibly allowing another task to acquire it and resume execution.

source