Multi-Threading
Base.Threads.@threads — MacroThreads.@threads [schedule] for ... endfor ループを並列に実行するためのマクロです。反復空間は粗粒度のタスクに分配されます。このポリシーは schedule 引数によって指定できます。ループの実行は、すべての反復の評価が完了するのを待ちます。
@threads によって生成されたタスクは :default スレッドプールでスケジュールされます。これは、@threads がメインスレッドまたはインタラクティブプールのタスクから呼び出された場合でも、:interactive スレッドプールのスレッドを使用しないことを意味します。:default スレッドプールは、計算集約型の並列ワークロードを対象としています。
関連情報: @spawn および Distributed の pmap。スレッドプールに関する詳細は、スレッドプール の章を参照してください。
Extended help
セマンティクス
スケジューリングオプションによってより強力な保証が指定されない限り、@threads マクロによって実行されるループは以下のセマンティクスを持ちます。
@threads マクロは、ループ本体を不特定の順序で、かつ潜在的に同時に実行します。タスクとワーカースレッドの正確な割り当ては指定されません。割り当ては各実行で異なる場合があります。ループ本体のコード(それから遷移的に呼び出されるコードを含む)は、タスクへの反復の分配や、それらが実行されるワーカースレッドについての仮定をしてはいけません。各反復のループ本体は、他の反復に依存せずに前進できる必要があり、データ競合から解放されている必要があります。そのため、反復間での無効な同期はデッドロックを引き起こす可能性があり、同期されていないメモリアクセスは未定義の動作を引き起こす可能性があります。
例えば、上記の条件は以下を示唆します:
- 反復で取得したロックは、同じ反復内で解放されなければなりません。
Channelのようなブロッキングプリミティブを使用して反復間で通信することは不正です。- 反復間で共有されていない場所にのみ書き込みます(ロックまたは原子操作が使用されていない限り)。
:staticスケジュールが使用されない限り、threadid()の値は単一の反復内でも変わる可能性があります。Task Migrationを参照してください。
スケジューラ
スケジューラ引数がない場合、正確なスケジューリングは未指定で、Julia のリリースによって異なります。現在、スケジューラが指定されていない場合は :dynamic が使用されます。
:dynamic (デフォルト)
:dynamic スケジューラは、利用可能なワーカースレッドに対して反復を動的に実行します。現在の実装は、各反復のワークロードが均一であると仮定しています。ただし、この仮定は将来的に削除される可能性があります。
このスケジューリングオプションは、基盤となる実行メカニズムへの単なるヒントです。ただし、いくつかの特性が期待できます。:dynamic スケジューラによって使用される Task の数は、利用可能なワーカースレッドの数の小さな定数倍に制限されています(Threads.threadpoolsize())。各タスクは反復空間の連続した領域を処理します。したがって、@threads :dynamic for x in xs; f(x); end は、length(xs) がワーカースレッドの数よりも大幅に大きく、f(x) の実行時間がタスクの生成と同期のコスト(通常は 10 マイクロ秒未満)よりも相対的に小さい場合、通常は @sync for x in xs; @spawn f(x); end よりも効率的です。
:greedy
:greedy スケジューラは、最大で Threads.threadpoolsize() タスクを生成し、生成される値に対して貪欲に作業を行います。1 つのタスクが作業を終えると、次の値をイテレータから取得します。個々のタスクが行う作業は、イテレータからの連続した値である必要はありません。与えられたイテレータは永遠に値を生成する可能性があり、イテレータインターフェースのみが必要です(インデックス付けは不要です)。
このスケジューリングオプションは、個々の反復のワークロードが均一でない/大きなばらつきがある場合に一般的に良い選択です。
:static
:static スケジューラは、スレッドごとに 1 つのタスクを作成し、反復を均等に分配し、各タスクを特定のスレッドに割り当てます。特に、threadid() の値は、1 つの反復内で一定であることが保証されています。別の @threads ループ内または 1 以外のスレッドから使用する場合、:static を指定することはエラーです。
:static スケジューリングは、Julia 1.3 より前に書かれたコードの移行をサポートするために存在します。新しく書かれたライブラリ関数では、:static スケジューリングは推奨されません。このオプションを使用する関数は、任意のワーカースレッドから呼び出すことができないためです。
例
異なるスケジューリング戦略を示すために、指定された秒数間実行される非待機のタイムループを含む次の関数 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 つが 1 秒の反復を 2 回実行して for ループを完了できるからです。 ```
Base.Threads.foreach — FunctionThreads.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.foreachは、FairScheduleよりもアイテムごとのオーバーヘッドが低くなるようにタスクを生成しますが、ロードバランスにはあまり適していません。このアプローチは、細かく均一なワークロードにより適している可能性がありますが、他のマルチスレッドワークロードと同時に実行する場合、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]Base.Threads.@spawn — MacroThreads.@spawn [:default|:interactive|:samepool] exprTask を作成し、指定されたスレッドプール :default、:interactive、または呼び出し元と同じプールを使用する :samepool で実行するように schedule します。指定されていない場合は :default が使用されます。タスクは、利用可能なスレッドが見つかると、そのスレッドに割り当てられます。タスクが完了するのを待つには、このマクロの結果に対して wait を呼び出すか、fetch を呼び出して待機し、その戻り値を取得します。
値は $ を介して @spawn に補間でき、これは値を構築された基盤のクロージャに直接コピーします。これにより、変数の値を挿入でき、現在のタスク内での変数の値の変更から非同期コードを隔離できます。
タスクが実行されるスレッドは、タスクが中断されると変更される可能性があるため、threadid() はタスクに対して定数として扱うべきではありません。詳細な重要な注意事項については、Task Migration およびより広範な multi-threading マニュアルを参照してください。また、threadpools に関する章も参照してください。
例
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 4Base.Threads.threadid — FunctionThreads.threadid([t::Task]) -> Int現在の実行スレッドのID番号、またはタスクtのスレッドのID番号を取得します。マスタースレッドのIDは1です。
例
julia> Threads.threadid()
1
julia> Threads.@threads for i in 1:4
println(Threads.threadid())
end
4
2
5
4
julia> Threads.threadid(Threads.@spawn "foo")
2タスクが実行されるスレッドは、タスクが中断されると変更される可能性があり、これをTask Migrationと呼びます。このため、ほとんどの場合、threadid([task])を使用して、たとえばバッファや状態を持つオブジェクトのベクターにインデックスを付けることは安全ではありません。
Base.Threads.maxthreadid — FunctionThreads.maxthreadid() -> IntJuliaプロセスで利用可能なスレッドの数(すべてのスレッドプールを通じて)の下限を取得します。これは、アトミック取得セマンティクスを持ちます。結果は常にthreadid()およびthreadid(task)(maxthreadidを呼び出す前に観察できた任意のタスクに対して)以上になります。
Base.Threads.nthreads — FunctionThreads.nthreads(:default | :interactive) -> Int指定されたスレッドプール内の現在のスレッド数を取得します。:interactive のスレッドは ID 番号 1:nthreads(:interactive) を持ち、:default のスレッドは nthreads(:interactive) .+ (1:nthreads(:default)) の ID 番号を持ちます。
BLAS.get_num_threads および BLAS.set_num_threads は LinearAlgebra 標準ライブラリに、nprocs() は Distributed 標準ライブラリに、そして Threads.maxthreadid() にも関連しています。
Base.Threads.threadpool — FunctionThreads.threadpool(tid = threadid()) -> Symbol指定されたスレッドのスレッドプールを返します。:default、:interactive、または:foreignのいずれかです。
Base.Threads.nthreadpools — FunctionThreads.nthreadpools() -> Int現在構成されているスレッドプールの数を返します。
Base.Threads.threadpoolsize — FunctionThreads.threadpoolsize(pool::Symbol = :default) -> Intデフォルトのスレッドプール(または指定されたスレッドプール)で利用可能なスレッドの数を取得します。
関連情報: LinearAlgebra 標準ライブラリの BLAS.get_num_threads および BLAS.set_num_threads、および Distributed 標準ライブラリの nprocs()。
Base.Threads.ngcthreads — FunctionThreads.ngcthreads() -> Int現在構成されているGCスレッドの数を返します。これには、マークスレッドと同時スイープスレッドの両方が含まれます。
See also Multi-Threading.
Atomic operations
atomic — KeywordUnsafe pointer operations are compatible with loading and storing pointers declared with _Atomic and std::atomic type in C11 and C++23 respectively. An error may be thrown if there is not support for atomically loading the Julia type T.
See also: unsafe_load, unsafe_modify!, unsafe_replace!, unsafe_store!, unsafe_swap!
Base.@atomic — Macro@atomic var
@atomic order exvar または 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
@atomic m[idx] = new
@atomic m[idx] += addend
@atomic :release m[idx] = new
@atomic :acquire_release m[idx] += addend右側で表現されたストア操作を原子的に実行し、新しい値を返します。
代入(=)の場合、この操作は setproperty!(a.b, :x, new) に変換されるか、参照の場合は setindex_atomic!(m, order, new, idx) 呼び出しに変換され、order のデフォルトは :sequentially_consistent です。
任意の修正演算子の場合、この操作は modifyproperty!(a.b, :x, op, addend)[2] に変換されるか、参照の場合は modifyindex_atomic!(m, order, op, addend, idx...)[2] 呼び出しに変換され、order のデフォルトは :sequentially_consistent です。
@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
@atomic m[idx] max arg2
@atomic m[idx] + arg2
@atomic max(m[idx], arg2)
@atomic :acquire_release max(m[idx], arg2)
@atomic :acquire_release m[idx] + arg2
@atomic :acquire_release m[idx] max arg2右側で表現された二項演算を原子的に実行します。結果を最初の引数のフィールドまたは参照に格納し、値 (old, new) を返します。
この操作は modifyproperty!(a.b, :x, func, arg2) に変換されるか、参照の場合は modifyindex_atomic!(m, order, func, arg2, idx) 呼び出しに変換され、order のデフォルトは :sequentially_consistent です。
詳細については、マニュアルの Per-field atomics セクションを参照してください。
例
julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomic a.x # fetch field x of a, with sequential consistency
1
julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency
2
julia> @atomic a.x += 1 # increment field x of a, with sequential consistency
3
julia> @atomic a.x + 1 # increment field x of a, with sequential consistency
3 => 4
julia> @atomic a.x # fetch field x of a, with sequential consistency
4
julia> @atomic max(a.x, 10) # change field x of a to the max value, with sequential consistency
4 => 10
julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency
10 => 10julia> mem = AtomicMemory{Int}(undef, 2);
julia> @atomic mem[1] = 2 # set mem[1] to value 2 with sequential consistency
2
julia> @atomic :monotonic mem[1] # fetch the first value of mem, with monotonic consistency
2
julia> @atomic mem[1] += 1 # increment the first value of mem, with sequential consistency
3
julia> @atomic mem[1] + 1 # increment the first value of mem, with sequential consistency
3 => 4
julia> @atomic mem[1] # fetch the first value of mem, with sequential consistency
4
julia> @atomic max(mem[1], 10) # change the first value of mem to the max value, with sequential consistency
4 => 10
julia> @atomic mem[1] max 5 # again change the first value of mem to the max value, with sequential consistency
10 => 10Base.@atomicswap — Macro@atomicswap a.b.x = new
@atomicswap :sequentially_consistent a.b.x = new
@atomicswap m[idx] = new
@atomicswap :sequentially_consistent m[idx] = newnewをa.b.x(参照の場合はm[idx])に格納し、a.b.xの古い値(それぞれm[idx]に格納されていた古い値)を返します。
この操作は、swapproperty!(a.b, :x, new)または、参照の場合はswapindex_atomic!(mem, order, new, idx)呼び出しに変換され、orderはデフォルトで:sequentially_consistentになります。
詳細については、マニュアルの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 # aのフィールドxを4に置き換え、順序の一貫性を持つ
1
julia> @atomic a.x # aのフィールドxを取得し、順序の一貫性を持つ
4julia> mem = AtomicMemory{Int}(undef, 2);
julia> @atomic mem[1] = 1;
julia> @atomicswap mem[1] = 4 # `mem`の最初の値を4に置き換え、順序の一貫性を持つ
1
julia> @atomic mem[1] # memの最初の値を取得し、順序の一貫性を持つ
4Base.@atomicreplace — Macro@atomicreplace a.b.x expected => desired
@atomicreplace :sequentially_consistent a.b.x expected => desired
@atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired
@atomicreplace m[idx] expected => desired
@atomicreplace :sequentially_consistent m[idx] expected => desired
@atomicreplace :sequentially_consistent :monotonic m[idx] expected => desiredペアによって表現された条件付き置換を原子的に実行し、値 (old, success::Bool) を返します。ここで success は置換が完了したかどうかを示します。
この操作は replaceproperty!(a.b, :x, expected, desired) または参照の場合は replaceindex_atomic!(mem, success_order, fail_order, expected, desired, idx) 呼び出しに変換され、両方の順序はデフォルトで :sequentially_consistent になります。
詳細についてはマニュアルの 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 => 3 # 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を取得し、順序は逐次的整合性
0julia> mem = AtomicMemory{Int}(undef, 2);
julia> @atomic mem[1] = 1;
julia> @atomicreplace mem[1] 1 => 2 # memの最初の値を1であれば2に置換し、順序は逐次的整合性
(old = 1, success = true)
julia> @atomic mem[1] # memの最初の値を取得し、順序は逐次的整合性
2
julia> @atomicreplace mem[1] 1 => 3 # aのフィールドxを1であれば2に置換し、順序は逐次的整合性
(old = 2, success = false)
julia> xchg = 2 => 0; # aのフィールドxを2であれば0に置換し、順序は逐次的整合性
julia> @atomicreplace mem[1] xchg
(old = 2, success = true)
julia> @atomic mem[1] # memの最初の値を取得し、順序は逐次的整合性
0Base.@atomiconce — Macro@atomiconce a.b.x = value
@atomiconce :sequentially_consistent a.b.x = value
@atomiconce :sequentially_consistent :monotonic a.b.x = value
@atomiconce m[idx] = value
@atomiconce :sequentially_consistent m[idx] = value
@atomiconce :sequentially_consistent :monotonic m[idx] = value値が以前に未設定であった場合、条件付きで値を原子的に割り当てます。返される値 success::Bool は、割り当てが完了したかどうかを示します。
この操作は setpropertyonce!(a.b, :x, value) に変換されるか、参照の場合は setindexonce_atomic!(m, success_order, fail_order, value, idx) 呼び出しに変換され、両方の順序はデフォルトで :sequentially_consistent になります。
詳細については、マニュアルの 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に設定します。未設定の場合、順序はsequential consistencyです。
true
julia> @atomic a.x # aのフィールドxを取得します。順序はsequential consistencyです。
1
julia> @atomiconce :monotonic a.x = 2 # aのフィールドxを1に設定します。未設定の場合、順序はmonotonic consistencyです。
falsejulia> mem = AtomicMemory{Vector{Int}}(undef, 1);
julia> isassigned(mem, 1)
false
julia> @atomiconce mem[1] = [1] # memの最初の値を[1]に設定します。未設定の場合、順序はsequential consistencyです。
true
julia> isassigned(mem, 1)
true
julia> @atomic mem[1] # memの最初の値を取得します。順序はsequential consistencyです。
1-element Vector{Int64}:
1
julia> @atomiconce :monotonic mem[1] = [2] # memの最初の値を[2]に設定します。未設定の場合、順序はmonotonicです。
false
julia> @atomic mem[1]
1-element Vector{Int64}:
1Core.AtomicMemory — TypeAtomicMemory{T} == GenericMemory{:atomic, T, Core.CPU}固定サイズの DenseVector{T}。その個々の要素への取得は原子的に行われます(デフォルトでは :monotonic 順序)。
AtomicMemory へのアクセスは、@atomic マクロを使用するか、低レベルのインターフェース関数 Base.getindex_atomic、Base.setindex_atomic!、Base.setindexonce_atomic!、Base.swapindex_atomic!、Base.modifyindex_atomic!、および Base.replaceindex_atomic! を使用して行う必要があります。
詳細については、Atomic Operations およびマクロ @atomic、@atomiconce、@atomicswap、および @atomicreplace を参照してください。
unsafe 関数のセットには、これらの原子操作の C/C++ 互換バージョンを選択するオプションのメモリ順序パラメータもあります。このパラメータが指定されている場合、unsafe_load、unsafe_store!、unsafe_swap!、unsafe_replace!、および unsafe_modify! が使用されます。
Base.Threads.Atomic — TypeThreads.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! — FunctionThreads.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! — FunctionThreads.atomic_xchg!(x::Atomic{T}, newval::T) where Txの値を原子的に交換します。
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[]
2Base.Threads.atomic_add! — FunctionThreads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypesvalを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[]
5Base.Threads.atomic_sub! — FunctionThreads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypesvalを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[]
1Base.Threads.atomic_and! — FunctionThreads.atomic_and!(x::Atomic{T}, val::T) where Txをvalと原子的にビット単位で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[]
2Base.Threads.atomic_nand! — FunctionThreads.atomic_nand!(x::Atomic{T}, val::T) where T原子的に x と val のビットワイズ NAND(非論理積)を行います。
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[]
-3Base.Threads.atomic_or! — FunctionThreads.atomic_or!(x::Atomic{T}, val::T) where Txをvalと原子的にビット単位で論理和します。
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[]
7Base.Threads.atomic_xor! — FunctionThreads.atomic_xor!(x::Atomic{T}, val::T) where T原子的に x と val のビット単位の排他的論理和 (XOR) を計算します。
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[]
2Base.Threads.atomic_max! — FunctionThreads.atomic_max!(x::Atomic{T}, val::T) where Txとvalの最大値を原子的に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[]
7Base.Threads.atomic_min! — FunctionThreads.atomic_min!(x::Atomic{T}, val::T) where Txとvalの最小値を原子的に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[]
5Base.Threads.atomic_fence — FunctionThreads.atomic_fence()逐次一致性メモリフェンスを挿入します
逐次一致性の順序セマンティクスを持つメモリフェンスを挿入します。これは、取得/解放の順序が不十分なアルゴリズムがあるため、必要です。
これは非常に高価な操作である可能性があります。Juliaの他のすべての原子操作にはすでに取得/解放セマンティクスがあるため、明示的なフェンスはほとんどの場合必要ないはずです。
詳細については、LLVMのfence命令を参照してください。
ccall using a libuv threadpool (Experimental)
Base.@threadcall — Macro@threadcall((cfunc, clib), rettype, (argtypes...), argvals...)@threadcallマクロはccallと同じ方法で呼び出されますが、異なるスレッドで作業を行います。これは、現在のjuliaスレッドをブロックさせることなく、ブロッキングC関数を呼び出したい場合に便利です。並行性はlibuvスレッドプールのサイズによって制限され、デフォルトでは4スレッドですが、UV_THREADPOOL_SIZE環境変数を設定し、juliaプロセスを再起動することで増やすことができます。
呼び出される関数は、決してJuliaにコールバックしてはいけないことに注意してください。
Low-level synchronization primitives
これらのビルディングブロックは、通常の同期オブジェクトを作成するために使用されます。
Base.Threads.SpinLock — TypeSpinLock()非再入型のテスト・アンド・テスト・アンド・セットスピンロックを作成します。再帰的な使用はデッドロックを引き起こします。この種のロックは、実行にかかる時間が短く、ブロックしないコード(例:I/Oを実行する)周辺でのみ使用するべきです。一般的には、ReentrantLock を代わりに使用するべきです。
各 lock は、unlock と対になっている必要があります。もし !islocked(lck::SpinLock) が成り立つ場合、trylock(lck) は、他のタスクが「同時に」ロックを保持しようとしていない限り成功します。
テスト・アンド・テスト・アンド・セットスピンロックは、約30程度の競合スレッドまでが最も速いです。それ以上の競合がある場合は、異なる同期アプローチを検討する必要があります。
Task metrics (Experimental)
Base.Experimental.task_metrics — FunctionBase.Experimental.task_metrics(::Bool)タスクごとのメトリクスの収集を有効または無効にします。Base.Experimental.task_metrics(true)が有効なときに作成されたTaskは、Base.Experimental.task_running_time_nsおよびBase.Experimental.task_wall_time_nsのタイミング情報を利用できます。
Base.Experimental.task_running_time_ns — FunctionBase.Experimental.task_running_time_ns(t::Task) -> Union{UInt64, Nothing}タスク t が実行に費やした合計ナノ秒を返します。このメトリックは、t が yield するか完了するまで更新されず、t が現在のタスクである場合は継続的に更新されます。詳細は Base.Experimental.task_wall_time_ns を参照してください。
タスクのタイミングが有効でない場合は nothing を返します。詳細は Base.Experimental.task_metrics を参照してください。
Base.Experimental.task_wall_time_ns — FunctionBase.Experimental.task_wall_time_ns(t::Task) -> Union{UInt64, Nothing}タスク t が実行可能であった合計ナノ秒を返します。これは、タスクが最初に実行キューに入ってから、完了するまでの時間、またはタスクがまだ完了していない場合は現在の時間までの時間です。 Base.Experimental.task_running_time_ns も参照してください。
タスクのタイミングが有効でない場合は nothing を返します。 Base.Experimental.task_metrics も参照してください。