Многопоточность
Base.Threads.@threadsМакрос
Threads.@threads [schedule] for ... end
Макрос для распараллеливания цикла for для выполнения с помощью нескольких потоков. Разбивает пространство итераций между несколькими задачами и выполняет эти задачи в потоках в соответствии с политикой планирования. В конце цикла устанавливается барьер, который ожидает завершения всех задач.
Аргумент schedule может быть использован для запроса определенной политики планирования. Единственное в настоящее время поддерживаемое значение — :static, которое создает одну задачу на поток и равномерно распределяет итерации между ними. Указание :static является ошибкой, если используется изнутри другого цикла @threads или из потока, отличного от 1.
Политика планирования по умолчанию (используемая, когда аргумент schedule отсутствует) может быть изменена.
Аргумент schedule доступен начиная с Julia 1.5.
Base.Threads.@spawnМакрос
Threads.@spawn expr
Создает и запускает Task в любом доступном потоке. Чтобы дождаться завершения задачи, вызовите wait на результате этого макроса или вызовите fetch для ожидания и получения возвращаемого значения.
Значения могут быть интерполированы в @spawn через $, что копирует значение напрямую в построенное базовое закрытие. Это позволяет вам вставлять значение переменной, изолируя асинхронный код от изменений значения переменной в текущей задаче.
См. главу руководства по многопоточности для важных замечаний.
Этот макрос доступен начиная с Julia 1.3.
Интерполяция значений через $ доступна начиная с Julia 1.4.
Base.Threads.threadidФункция
Threads.threadid()
Получает номер идентификатора текущего потока выполнения. Главный поток имеет идентификатор 1.
Base.Threads.nthreadsФункция
Threads.nthreads()
Получает количество потоков, доступных для процесса Julia. Это верхняя граница для threadid().
Синхронизация
Base.Threads.ConditionТип
Threads.Condition([lock])
Потокобезопасная версия Base.Condition.
Чтобы вызвать wait или notify на Threads.Condition, необходимо сначала вызвать lock на нём. При вызове wait, блокировка атомарно высвобождается во время блокировки, и будет повторно приобретена до возврата wait. Поэтому типичное использование Threads.Condition c выглядит следующим образом:
lock(c)
try
while !thing_we_are_waiting_for
wait(c)
end
finally
unlock(c)
end
Данный функционал требует как минимум Julia 1.2.
Base.EventТип
Event()
Создаёт триггерное событие. Задачи, которые вызывают wait на Event, приостанавливаются и ставятся в очередь, пока не будет вызван notify на Event. После вызова notify, Event остаётся в сигнализированном состоянии, и задачи больше не будут блокироваться при ожидании его.
Этот функционал требует как минимум Julia 1.1.
См. также Синхронизация.
Атомарные операции
API для атомарных операций ещё не окончательно определён и, вероятно, будет изменён.
Base.Threads.AtomicТип
Threads.Atomic{T}
Хранит ссылку на объект типа T, гарантируя, что к нему обращаются только атомарно, т.е. потокобезопасно.
Только определённые "простые" типы могут быть использованы атомарно, а именно примитивные булевы, целочисленные и числовые типы с плавающей точкой. Это Bool, Int8...Int128, UInt8...UInt128, и Float16...Float64.
Новые атомарные объекты могут быть созданы из неатомарных значений; если значение не указано, атомарный объект инициализируется нулём.
К атомарным объектам можно получить доступ с использованием обозначения [].
Примеры
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> x[] = 1
1
julia> x[]
1
Атомарные операции используют префикс atomic_, такой как atomic_add!, atomic_xchg! и т.д.
Base.Threads.atomic_cas!Функция
Threads.atomic_cas!(x::Atomic{T}, cmp::T, newval::T) where T
Атомарно сравнивает и устанавливает x
Атомарно сравнивает значение в x со значением cmp. Если они равны, записывает newval в x. В противном случае, оставляет x без изменений. Возвращает старое значение в x. Сравнивая возвращённое значение с cmp (через ===) можно узнать, было ли x изменено и теперь ли оно содержит новое значение newval.
Для получения дополнительных сведений см. инструкцию LLVM cmpxchg.
Эта функция может использоваться для реализации транзакционных семантик. Перед транзакцией записывается значение в x. После транзакции новое значение сохраняется только в том случае, если x не было изменено в промежутке.
Примеры
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)
исходный код
Base.Threads.atomic_xchg!Функция
Threads.atomic_xchg!(x::Atomic{T}, newval::T) where T
Атомарно меняет значение в x
Атомарно меняет значение в x на newval. Возвращает старое значение.
Для получения дополнительных сведений см. инструкцию LLVM atomicrmw xchg.
Примеры
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> Threads.atomic_xchg!(x, 2)
3
julia> x[]
2
исходный код
Base.Threads.atomic_add!Функция
Threads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes
Атомарно добавляет val к x
Выполняет x[] += val атомарно. Возвращает старое значение. Не определено для Atomic{Bool}.
Для получения дополнительных сведений см. инструкцию LLVM atomicrmw add.
Примеры
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> Threads.atomic_add!(x, 2)
3
julia> x[]
5
исходный код
Base.Threads.atomic_sub!Функция
Threads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes
Атомарно вычитает val из x
Выполняет x[] -= val атомарно. Возвращает старое значение. Не определено для Atomic{Bool}.
Для получения дополнительных сведений см. инструкцию LLVM atomicrmw sub.
Примеры
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> Threads.atomic_sub!(x, 2)
3
julia> x[]
1
исходный код
Base.Threads.atomic_and!Функция
Threads.atomic_and!(x::Atomic{T}, val::T) where T
Атомарно побитово и x с val
Выполняет x[] &= val атомарно. Возвращает старое значение.
Для получения дополнительных сведений см. инструкцию LLVM atomicrmw and.
Примеры
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> Threads.atomic_and!(x, 2)
3
julia> x[]
2
исходный код
Base.Threads.atomic_nand!Функция
Threads.atomic_nand!(x::Atomic{T}, val::T) where T
Атомарное побитовое-nand (не-и) x с val
Выполняет x[] = ~(x[] & val) атомарно. Возвращает значение old.
Для получения дополнительной информации см. инструкцию LLVM atomicrmw nand.
Примеры
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> Threads.atomic_nand!(x, 2)
3
julia> x[]
-3
исходный код
Base.Threads.atomic_or!Функция
Threads.atomic_or!(x::Atomic{T}, val::T) where T
Атомарное побитовое-или x с val
Выполняет x[] |= val атомарно. Возвращает значение old.
Для получения дополнительной информации см. инструкцию LLVM atomicrmw or.
Примеры
julia> x = Threads.Atomic{Int}(5)
Base.Threads.Atomic{Int64}(5)
julia> Threads.atomic_or!(x, 7)
5
julia> x[]
7
исходный код
Base.Threads.atomic_xor!Функция
Threads.atomic_xor!(x::Atomic{T}, val::T) where T
Атомарное побитовое-исключающее ИЛИ (XOR) x с val
Выполняет x[] $= val атомарно. Возвращает значение old.
Для получения дополнительной информации см. инструкцию LLVM atomicrmw xor.
Примеры
julia> x = Threads.Atomic{Int}(5)
Base.Threads.Atomic{Int64}(5)
julia> Threads.atomic_xor!(x, 7)
5
julia> x[]
2
исходный код
Base.Threads.atomic_max!Функция
Threads.atomic_max!(x::Atomic{T}, val::T) where T
Атомарно сохраняет максимальное значение из x и val в x
Выполняет x[] = max(x[], val) атомарно. Возвращает значение old.
Для получения дополнительной информации см. инструкцию LLVM atomicrmw max.
Примеры
julia> x = Threads.Atomic{Int}(5)
Base.Threads.Atomic{Int64}(5)
julia> Threads.atomic_max!(x, 7)
5
julia> x[]
7
исходный код
Base.Threads.atomic_min!Функция
Threads.atomic_min!(x::Atomic{T}, val::T) where T
Атомарно сохраняет минимальное значение из x и val в x
Выполняет x[] = min(x[], val) атомарно. Возвращает значение old.
Для получения дополнительной информации см. инструкцию LLVM atomicrmw min.
Примеры
julia> x = Threads.Atomic{Int}(7)
Base.Threads.Atomic{Int64}(7)
julia> Threads.atomic_min!(x, 5)
7
julia> x[]
5
исходный код
Base.Threads.atomic_fenceФункция
Threads.atomic_fence()
Вставка барьера согласованности последовательности памяти
Вставляет барьер памяти с семантикой согласованности последовательности. В некоторых алгоритмах это необходимо, например, там, где порядок "acquire/release" недостаточен.
Вероятно, это очень дорогостоящая операция. Учитывая, что все атомарные операции в Julia уже имеют семантику "acquire/release", явные барьеры в большинстве случаев не нужны.
Для получения дополнительной информации см. инструкцию LLVM fence.
ccall с использованием пула потоков (Экспериментально)
Base.@threadcallМакрос
@threadcall((cfunc, clib), rettype, (argtypes...), argvals...)
Макрос @threadcall вызывается так же, как ccall, но выполняет работу в другом потоке. Это полезно, когда вы хотите вызвать блокирующую функцию C, не блокируя основной julia поток. Конкурентность ограничена размером пула потоков libuv, который по умолчанию составляет 4 потока, но может быть увеличен установкой переменной окружения UV_THREADPOOL_SIZE и перезапуском процесса julia.
Обратите внимание, что вызываемая функция никогда не должна вызывать обратно в Julia.
исходный кодПримитивы низкоуровневой синхронизации
Эти строительные блоки используются для создания обычных объектов синхронизации.
Base.Threads.SpinLockТип
SpinLock()
Создает нерекурсивную блокировку с вращением, которая проверяет и проверяет и устанавливает. Рекурсивное использование приведет к тупику. Этот тип блокировки следует использовать только для кода, который выполняется быстро и не блокируется (например, выполняет ввод-вывод). В общем случае следует использовать ReentrantLock.
Каждый lock должен соответствовать unlock.
Блокировки с вращением являются наиболее быстрыми до примерно 30-ти конкурирующих потоков. Если у вас больше конкурирующих потоков, следует рассмотреть другие подходы к синхронизации.
исходный код
© 2009–2020 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.5.3/base/multi-threading/