Multi-Threading

Base.Threads.@threadsMacro
Threads.@threads [schedule] for ... end

병렬로 for 루프를 실행하는 매크로입니다. 반복 공간은 거칠게 분할된 작업에 분배됩니다. 이 정책은 schedule 인수로 지정할 수 있습니다. 루프의 실행은 모든 반복의 평가를 기다립니다.

참고: @spawnDistributedpmap.

확장 도움말

의미론

스케줄링 옵션에 의해 더 강력한 보장이 지정되지 않는 한, @threads 매크로에 의해 실행되는 루프는 다음과 같은 의미론을 가집니다.

@threads 매크로는 루프 본문을 불특정한 순서로 잠재적으로 동시에 실행합니다. 작업과 작업자 스레드의 정확한 할당을 지정하지 않습니다. 할당은 각 실행마다 다를 수 있습니다. 루프 본문 코드(그로부터 전이적으로 호출되는 모든 코드를 포함)는 반복이 작업에 분배되는 방식이나 실행되는 작업자 스레드에 대한 가정을 해서는 안 됩니다. 각 반복의 루프 본문은 다른 반복과 독립적으로 진행할 수 있어야 하며 데이터 경쟁으로부터 자유로워야 합니다. 따라서 반복 간의 잘못된 동기화는 교착 상태를 초래할 수 있으며, 동기화되지 않은 메모리 접근은 정의되지 않은 동작을 초래할 수 있습니다.

예를 들어, 위의 조건은 다음을 의미합니다:

  • 반복에서 획득한 잠금은 반드시 같은 반복 내에서 해제되어야 합니다.
  • Channel과 같은 차단 원시를 사용하여 반복 간에 통신하는 것은 잘못된 것입니다.
  • 반복 간에 공유되지 않는 위치에만 쓰기(잠금 또는 원자적 작업이 사용되지 않는 한).
  • :static 스케줄이 사용되지 않는 한, threadid()의 값은 단일 반복 내에서도 변경될 수 있습니다. Task Migration을 참조하십시오.

스케줄러

스케줄러 인수가 없으면 정확한 스케줄링은 지정되지 않으며 Julia 릴리스에 따라 다릅니다. 현재는 스케줄러가 지정되지 않은 경우 :dynamic이 사용됩니다.

Julia 1.5

schedule 인수는 Julia 1.5부터 사용할 수 있습니다.

:dynamic (기본값)

:dynamic 스케줄러는 사용 가능한 작업자 스레드에 대해 반복을 동적으로 실행합니다. 현재 구현은 각 반복의 작업 부하가 균일하다고 가정합니다. 그러나 이 가정은 미래에 제거될 수 있습니다.

이 스케줄링 옵션은 기본 실행 메커니즘에 대한 힌트일 뿐입니다. 그러나 몇 가지 속성을 기대할 수 있습니다. :dynamic 스케줄러가 사용하는 Task의 수는 사용 가능한 작업자 스레드의 수의 작은 상수 배수로 제한됩니다 (Threads.threadpoolsize()). 각 작업은 반복 공간의 인접한 영역을 처리합니다. 따라서 @threads :dynamic for x in xs; f(x); end는 일반적으로 @sync for x in xs; @spawn f(x); end보다 더 효율적입니다. 여기서 length(xs)는 작업자 스레드의 수보다 훨씬 크고 f(x)의 실행 시간은 작업을 생성하고 동기화하는 비용보다 상대적으로 작습니다(일반적으로 10마이크로초 미만).

Julia 1.8

schedule 인수에 대한 :dynamic 옵션은 Julia 1.8부터 사용 가능하며 기본값입니다.

:greedy

:greedy 스케줄러는 최대 Threads.threadpoolsize() 작업을 생성하며, 각 작업은 생성되는 주어진 반복 값에 대해 탐욕적으로 작업합니다. 하나의 작업이 작업을 마치면 다음 값을 반복자에서 가져옵니다. 개별 작업이 수행하는 작업은 반복자에서 인접한 값에 대해 반드시 수행되는 것은 아닙니다. 주어진 반복자는 영원히 값을 생성할 수 있으며, 반복자 인터페이스만 필요합니다(인덱싱 없음).

이 스케줄링 옵션은 일반적으로 개별 반복의 작업 부하가 균일하지 않거나 큰 분포를 가질 때 좋은 선택입니다.

Julia 1.11

schedule 인수에 대한 :greedy 옵션은 Julia 1.11부터 사용 가능합니다.

:static

:static 스케줄러는 각 스레드당 하나의 작업을 생성하고 반복을 균등하게 나누어 각 작업을 특정 스레드에 할당합니다. 특히, threadid()의 값은 하나의 반복 내에서 일정하다고 보장됩니다. 다른 @threads 루프 내에서 사용하거나 1이 아닌 스레드에서 사용하면 :static을 지정하는 것은 오류입니다.

Note

:static 스케줄링은 Julia 1.3 이전에 작성된 코드의 전환을 지원하기 위해 존재합니다. 새로 작성된 라이브러리 함수에서는 이 옵션을 사용하는 것이 권장되지 않습니다. 이 옵션을 사용하는 함수는 임의의 작업자 스레드에서 호출할 수 없습니다.

예제

다양한 스케줄링 전략을 설명하기 위해, 주어진 초 수 동안 실행되는 비양보 타이머 루프를 포함하는 다음 함수 busywait를 고려하십시오.

julia> function busywait(seconds)
            tstart = time_ns()
            while (time_ns() - tstart) / 1e9 < seconds
            end
        end

julia> @time begin
            Threads.@spawn busywait(5)
            Threads.@threads :static for i in 1:Threads.threadpoolsize()
                busywait(1)
            end
        end
6.003001 seconds (16.33 k allocations: 899.255 KiB, 0.25% compilation time)

julia> @time begin
            Threads.@spawn busywait(5)
            Threads.@threads :dynamic for i in 1:Threads.threadpoolsize()
                busywait(1)
            end
        end
2.012056 seconds (16.05 k allocations: 883.919 KiB, 0.66% compilation time)

:dynamic 예제는 2초가 걸리며, 비어 있는 스레드 중 하나가 1초 반복 두 개를 실행하여 for 루프를 완료할 수 있습니다. ```

source
Base.Threads.foreachFunction
Threads.foreach(f, channel::Channel;
                schedule::Threads.AbstractSchedule=Threads.FairSchedule(),
                ntasks=Threads.threadpoolsize())

foreach(f, channel)와 유사하지만, channel에 대한 반복과 f에 대한 호출이 Threads.@spawn에 의해 생성된 ntasks 작업에 분산됩니다. 이 함수는 내부적으로 생성된 모든 작업이 완료될 때까지 기다린 후 반환됩니다.

schedule isa FairSchedule인 경우, Threads.foreach는 Julia의 스케줄러가 스레드 간에 작업 항목을 보다 자유롭게 로드 밸런싱할 수 있도록 작업을 생성하려고 시도합니다. 이 접근 방식은 일반적으로 항목당 오버헤드가 더 높지만, 다른 멀티스레드 작업과 동시에 수행할 때 StaticSchedule보다 더 나은 성능을 발휘할 수 있습니다.

schedule isa StaticSchedule인 경우, Threads.foreachFairSchedule보다 항목당 오버헤드가 낮은 방식으로 작업을 생성하지만, 로드 밸런싱에는 덜 적합합니다. 따라서 이 접근 방식은 세밀하고 균일한 작업에 더 적합할 수 있지만, 다른 멀티스레드 작업과 동시에 수행할 때 FairSchedule보다 성능이 떨어질 수 있습니다.

예제

julia> n = 20

julia> c = Channel{Int}(ch -> foreach(i -> put!(ch, i), 1:n), 1)

julia> d = Channel{Int}(n) do ch
           f = i -> put!(ch, i^2)
           Threads.foreach(f, c)
       end

julia> collect(d)
collect(d) = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400]
Julia 1.6

이 함수는 Julia 1.6 이상이 필요합니다.

source
Base.Threads.@spawnMacro
Threads.@spawn [:default|:interactive] expr

Task를 생성하고 지정된 스레드 풀에서 사용 가능한 스레드에서 실행되도록 schedule합니다(:default가 지정되지 않은 경우). 작업은 스레드가 사용 가능해지면 해당 스레드에 할당됩니다. 작업이 완료될 때까지 기다리려면 이 매크로의 결과에 대해 wait를 호출하거나, fetch를 호출하여 기다린 후 반환 값을 얻습니다.

값은 $를 통해 @spawn에 삽입될 수 있으며, 이는 값을 생성된 기본 클로저에 직접 복사합니다. 이를 통해 변수의 을 삽입할 수 있으며, 현재 작업에서 변수의 값 변경으로부터 비동기 코드를 격리할 수 있습니다.

Note

작업이 실행되는 스레드는 작업이 양보할 경우 변경될 수 있으므로, threadid()는 작업에 대해 상수로 취급해서는 안 됩니다. Task Migration 및 더 넓은 multi-threading 매뉴얼에서 추가적인 중요한 주의 사항을 참조하십시오. threadpools 장도 참조하십시오.

Julia 1.3

이 매크로는 Julia 1.3부터 사용할 수 있습니다.

Julia 1.4

$를 통한 값의 인터폴레이션은 Julia 1.4부터 사용할 수 있습니다.

Julia 1.9

스레드 풀은 Julia 1.9부터 지정할 수 있습니다.

예제

julia> t() = println("Hello from ", Threads.threadid());

julia> tasks = fetch.([Threads.@spawn t() for i in 1:4]);
Hello from 1
Hello from 1
Hello from 3
Hello from 4
source
Base.Threads.threadidFunction
Threads.threadid() -> Int

현재 실행 중인 스레드의 ID 번호를 가져옵니다. 마스터 스레드는 ID 1을 가집니다.

예제

julia> Threads.threadid()
1

julia> Threads.@threads for i in 1:4
          println(Threads.threadid())
       end
4
2
5
4
Note

작업이 양보할 경우 작업이 실행되는 스레드는 변경될 수 있으며, 이는 Task Migration으로 알려져 있습니다. 이러한 이유로 대부분의 경우 threadid()를 사용하여 버퍼 또는 상태 객체의 벡터에 인덱싱하는 것은 안전하지 않습니다.

source
Base.Threads.maxthreadidFunction
Threads.maxthreadid() -> Int

Julia 프로세스에서 사용 가능한 스레드 수(모든 스레드 풀에 걸쳐)에 대한 하한을 가져옵니다. 원자적 획득 의미론을 사용합니다. 결과는 항상 threadid()threadid(task)보다 크거나 같으며, 이는 maxthreadid를 호출하기 전에 관찰할 수 있었던 모든 작업에 해당합니다.

source
Base.Threads.nthreadsFunction
Threads.nthreads(:default | :interactive) -> Int

지정된 스레드 풀 내에서 현재 스레드 수를 가져옵니다. :interactive의 스레드는 ID 번호가 1:nthreads(:interactive)이고, :default의 스레드는 ID 번호가 nthreads(:interactive) .+ (1:nthreads(:default))입니다.

또한 LinearAlgebra 표준 라이브러리의 BLAS.get_num_threadsBLAS.set_num_threads, 그리고 Distributed 표준 라이브러리의 nprocs()Threads.maxthreadid()를 참조하십시오.

source
Base.Threads.threadpoolFunction
Threads.threadpool(tid = threadid()) -> Symbol

지정된 스레드의 스레드 풀을 반환합니다. :default, :interactive 또는 :foreign 중 하나입니다.

source
Base.Threads.threadpoolsizeFunction
Threads.threadpoolsize(pool::Symbol = :default) -> Int

기본 스레드 풀(또는 지정된 스레드 풀)에 사용할 수 있는 스레드 수를 가져옵니다.

또한, LinearAlgebra 표준 라이브러리의 BLAS.get_num_threadsBLAS.set_num_threads, 그리고 Distributed 표준 라이브러리의 nprocs()를 참조하십시오.

source
Base.Threads.ngcthreadsFunction
Threads.ngcthreads() -> Int

현재 구성된 GC 스레드의 수를 반환합니다. 여기에는 마크 스레드와 동시 스위프 스레드가 모두 포함됩니다.

source

또한 Multi-Threading를 참조하세요.

Atomic operations

atomicKeyword

안전하지 않은 포인터 작업은 C11 및 C++23에서 각각 _Atomicstd::atomic 유형으로 선언된 포인터를 로드하고 저장하는 것과 호환됩니다. Julia 유형 T를 원자적으로 로드하는 것을 지원하지 않는 경우 오류가 발생할 수 있습니다.

참고: unsafe_load, unsafe_modify!, unsafe_replace!, unsafe_store!, unsafe_swap!

source
Base.@atomicMacro
@atomic var
@atomic order ex

var 또는 ex를 원자적으로 수행되도록 표시합니다. ex가 지원되는 표현식인 경우에만 해당합니다. order가 지정되지 않으면 기본값은 :sequentially_consistent입니다.

@atomic a.b.x = new
@atomic a.b.x += addend
@atomic :release a.b.x = new
@atomic :acquire_release a.b.x += addend

오른쪽에 표현된 저장 작업을 원자적으로 수행하고 새 값을 반환합니다.

=를 사용하면 이 작업은 setproperty!(a.b, :x, new) 호출로 변환됩니다. 다른 연산자를 사용할 경우 이 작업은 modifyproperty!(a.b, :x, +, addend)[2] 호출로 변환됩니다.

@atomic a.b.x max arg2
@atomic a.b.x + arg2
@atomic max(a.b.x, arg2)
@atomic :acquire_release max(a.b.x, arg2)
@atomic :acquire_release a.b.x + arg2
@atomic :acquire_release a.b.x max arg2

오른쪽에 표현된 이진 작업을 원자적으로 수행합니다. 결과를 첫 번째 인수의 필드에 저장하고 값 (old, new)를 반환합니다.

이 작업은 modifyproperty!(a.b, :x, func, arg2) 호출로 변환됩니다.

자세한 내용은 매뉴얼의 Per-field atomics 섹션을 참조하십시오.

예제

julia> mutable struct Atomic{T}; @atomic x::T; end

julia> a = Atomic(1)
Atomic{Int64}(1)

julia> @atomic a.x # a의 필드 x를 가져오고, 순차적 일관성으로
1

julia> @atomic :sequentially_consistent a.x = 2 # a의 필드 x를 설정하고, 순차적 일관성으로
2

julia> @atomic a.x += 1 # a의 필드 x를 증가시키고, 순차적 일관성으로
3

julia> @atomic a.x + 1 # a의 필드 x를 증가시키고, 순차적 일관성으로
3 => 4

julia> @atomic a.x # a의 필드 x를 가져오고, 순차적 일관성으로
4

julia> @atomic max(a.x, 10) # a의 필드 x를 최대값으로 변경하고, 순차적 일관성으로
4 => 10

julia> @atomic a.x max 5 # 다시 a의 필드 x를 최대값으로 변경하고, 순차적 일관성으로
10 => 10
Julia 1.7

이 기능은 최소한 Julia 1.7이 필요합니다.

source
Base.@atomicswapMacro
@atomicswap a.b.x = new
@atomicswap :sequentially_consistent a.b.x = new

newa.b.x에 저장하고 a.b.x의 이전 값을 반환합니다.

이 작업은 swapproperty!(a.b, :x, new) 호출로 변환됩니다.

자세한 내용은 매뉴얼의 Per-field atomics 섹션을 참조하세요.

예제

julia> mutable struct Atomic{T}; @atomic x::T; end

julia> a = Atomic(1)
Atomic{Int64}(1)

julia> @atomicswap a.x = 2+2 # sequential consistency로 a의 필드 x를 4로 교체
1

julia> @atomic a.x # sequential consistency로 a의 필드 x를 가져옴
4
Julia 1.7

이 기능은 최소한 Julia 1.7이 필요합니다.

source
Base.@atomicreplaceMacro
@atomicreplace a.b.x expected => desired
@atomicreplace :sequentially_consistent a.b.x expected => desired
@atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired

조건부 교체를 쌍으로 원자적으로 수행하여 (old, success::Bool) 값을 반환합니다. 여기서 success는 교체가 완료되었는지를 나타냅니다.

이 작업은 replaceproperty!(a.b, :x, expected, desired) 호출로 변환됩니다.

자세한 내용은 매뉴얼의 Per-field atomics 섹션을 참조하세요.

예제

julia> mutable struct Atomic{T}; @atomic x::T; end

julia> a = Atomic(1)
Atomic{Int64}(1)

julia> @atomicreplace a.x 1 => 2 # a의 필드 x를 1일 경우 2로 교체, 순차적 일관성 유지
(old = 1, success = true)

julia> @atomic a.x # a의 필드 x를 가져오기, 순차적 일관성 유지
2

julia> @atomicreplace a.x 1 => 2 # a의 필드 x를 1일 경우 2로 교체, 순차적 일관성 유지
(old = 2, success = false)

julia> xchg = 2 => 0; # a의 필드 x를 2일 경우 0으로 교체, 순차적 일관성 유지

julia> @atomicreplace a.x xchg
(old = 2, success = true)

julia> @atomic a.x # a의 필드 x를 가져오기, 순차적 일관성 유지
0
Julia 1.7

이 기능은 최소한 Julia 1.7이 필요합니다.

source
Base.@atomiconceMacro
@atomiconce a.b.x = value
@atomiconce :sequentially_consistent a.b.x = value
@atomiconce :sequentially_consistent :monotonic a.b.x = value

값이 이전에 설정되지 않은 경우, 조건부 할당을 원자적으로 수행하고 success::Bool 값을 반환합니다. 여기서 success는 할당이 완료되었는지를 나타냅니다.

이 작업은 setpropertyonce!(a.b, :x, value) 호출로 변환됩니다.

자세한 내용은 매뉴얼의 Per-field atomics 섹션을 참조하십시오.

예제

julia> mutable struct AtomicOnce
           @atomic x
           AtomicOnce() = new()
       end

julia> a = AtomicOnce()
AtomicOnce(#undef)

julia> @atomiconce a.x = 1 # 설정되지 않은 경우, 순차적 일관성으로 a의 필드 x를 1로 설정
true

julia> @atomic a.x # 순차적 일관성으로 a의 필드 x를 가져오기
1

julia> @atomiconce a.x = 1 # 설정되지 않은 경우, 순차적 일관성으로 a의 필드 x를 1로 설정
false
Julia 1.11

이 기능은 최소한 Julia 1.11이 필요합니다.

```

source
Core.AtomicMemoryType
AtomicMemory{T} == GenericMemory{:atomic, T, Core.CPU}

고정 크기 DenseVector{T}. 그 요소에 대한 접근은 원자적으로 수행됩니다( :monotonic 순서로). 요소 중 하나를 설정하려면 @atomic 매크로를 사용하고 명시적으로 순서를 지정해야 합니다.

!!! 경고 각 요소는 접근할 때 독립적으로 원자적이며 비원자적으로 설정할 수 없습니다. 현재 @atomic 매크로와 고급 인터페이스는 완료되지 않았지만, 향후 구현을 위한 빌딩 블록은 내부 내장 함수 Core.memoryrefget, Core.memoryrefset!, Core.memoryref_isassigned, Core.memoryrefswap!, Core.memoryrefmodify!, 및 Core.memoryrefreplace!입니다.

자세한 내용은 Atomic Operations를 참조하십시오.

!!! 호환성 "Julia 1.11" 이 유형은 Julia 1.11 이상이 필요합니다.

source

unsafe 함수 집합에 대한 선택적 메모리 정렬 매개변수가 있으며, 이 매개변수가 지정되면 이러한 원자적 작업의 C/C++ 호환 버전을 선택합니다. 매개변수는 다음과 같습니다: unsafe_load, unsafe_store!, unsafe_swap!, unsafe_replace!, 및 unsafe_modify!.

Warning

다음 API는 더 이상 사용되지 않지만, 여러 릴리스 동안 지원이 유지될 가능성이 높습니다.

Base.Threads.AtomicType
Threads.Atomic{T}

타입 T의 객체에 대한 참조를 보유하며, 이는 원자적으로 접근되도록 보장합니다. 즉, 스레드 안전한 방식으로 접근됩니다.

원자적으로 사용할 수 있는 "단순한" 타입은 제한적이며, 기본적인 불리언, 정수 및 부동 소수점 타입입니다. 이들은 Bool, Int8...Int128, UInt8...UInt128, 및 Float16...Float64입니다.

새로운 원자 객체는 비원자 값에서 생성할 수 있으며, 지정하지 않으면 원자 객체는 0으로 초기화됩니다.

원자 객체는 [] 표기법을 사용하여 접근할 수 있습니다:

예제

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

julia> x[] = 1
1

julia> x[]
1

원자 작업은 atomic_ 접두사를 사용하며, 예를 들어 atomic_add!, atomic_xchg! 등이 있습니다.

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

원자적으로 x를 비교하고 설정합니다.

x의 값을 cmp와 원자적으로 비교합니다. 같으면 newvalx에 씁니다. 그렇지 않으면 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)
source
Base.Threads.atomic_xchg!Function
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
source
Base.Threads.atomic_add!Function
Threads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes

valx에 원자적으로 더합니다.

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
source
Base.Threads.atomic_sub!Function
Threads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes

valx에서 원자적으로 빼기

원자적으로 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
source
Base.Threads.atomic_and!Function
Threads.atomic_and!(x::Atomic{T}, val::T) where T

xval을 원자적으로 비트 단위 AND 연산합니다.

원자적으로 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
source
Base.Threads.atomic_nand!Function
Threads.atomic_nand!(x::Atomic{T}, val::T) where T

원자적으로 비트 단위 nand (not-and) 연산을 xval에 수행합니다.

x[] = ~(x[] & val)를 원자적으로 수행합니다. 이전 값을 반환합니다.

자세한 내용은 LLVM의 atomicrmw nand 명령어를 참조하십시오.

예제

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

xval과 원자적으로 비트wise-or합니다.

원자적으로 x[] |= val을 수행합니다. 이전 값을 반환합니다.

자세한 내용은 LLVM의 atomicrmw or 명령어를 참조하십시오.

예제

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

원자적으로 xval을 비트 단위 XOR(배타적 OR)합니다.

x[] $= val을 원자적으로 수행합니다. 이전 값을 반환합니다.

자세한 내용은 LLVM의 atomicrmw xor 명령어를 참조하십시오.

예제

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

xval의 최대값을 원자적으로 x에 저장합니다.

원자적으로 x[] = max(x[], val)을 수행합니다. 이전 값을 반환합니다.

자세한 내용은 LLVM의 atomicrmw max 명령어를 참조하십시오.

예제

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

xval의 최소값을 원자적으로 x에 저장합니다.

x[] = min(x[], val)을 원자적으로 수행합니다. 이전 값을 반환합니다.

자세한 내용은 LLVM의 atomicrmw min 명령어를 참조하십시오.

예제

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_fenceFunction
Threads.atomic_fence()

순차적 일관성 메모리 펜스 삽입

순차적으로 일관된 순서 의미를 가진 메모리 펜스를 삽입합니다. 이는 필요할 수 있는 알고리즘이 있으며, 즉 acquire/release 순서만으로는 충분하지 않은 경우입니다.

이는 아마도 매우 비싼 작업일 것입니다. Julia의 다른 모든 원자 작업이 이미 acquire/release 의미를 가지고 있으므로, 대부분의 경우 명시적인 펜스는 필요하지 않을 것입니다.

자세한 내용은 LLVM의 fence 명령어를 참조하십시오.

source

ccall using a libuv threadpool (Experimental)

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

@threadcall 매크로는 ccall와 같은 방식으로 호출되지만, 다른 스레드에서 작업을 수행합니다. 이는 현재 julia 스레드가 차단되지 않도록 차단 C 함수를 호출하고자 할 때 유용합니다. 동시성은 libuv 스레드 풀의 크기에 의해 제한되며, 기본값은 4개의 스레드이지만 UV_THREADPOOL_SIZE 환경 변수를 설정하고 julia 프로세스를 재시작하여 늘릴 수 있습니다.

호출된 함수는 절대 Julia로 다시 호출해서는 안 됩니다.

source

Low-level synchronization primitives

이 빌딩 블록은 정기적인 동기화 객체를 생성하는 데 사용됩니다.

Base.Threads.SpinLockType
SpinLock()

비재진입, 테스트-테스트-세트 스핀 잠금을 생성합니다. 재귀적 사용은 교착 상태를 초래합니다. 이러한 종류의 잠금은 실행하는 데 시간이 거의 걸리지 않고 차단하지 않는 코드(예: I/O 수행) 주위에서만 사용해야 합니다. 일반적으로 ReentrantLock을 대신 사용해야 합니다.

lockunlock과 쌍을 이루어야 합니다. !islocked(lck::SpinLock)이 참이면, trylock(lck)은 "동시에" 잠금을 보유하려고 시도하는 다른 작업이 없으면 성공합니다.

테스트-테스트-세트 스핀 잠금은 약 30개 정도의 경쟁 스레드까지 가장 빠릅니다. 그보다 더 많은 경쟁이 있는 경우, 다른 동기화 접근 방식을 고려해야 합니다.

source