Multi-Threading
Base.Threads.@threads
— MacroThreads.@threads [schedule] for ... end
Paralel olarak bir for
döngüsü çalıştırmak için bir makro. İterasyon alanı, kaba-granüllü görevlere dağıtılır. Bu politika schedule
argümanı ile belirtilebilir. Döngünün yürütülmesi, tüm iterasyonların değerlendirilmesini bekler.
Ayrıca bakınız: @spawn
ve Distributed
içindeki pmap
.
Genişletilmiş yardım
Anlam
Planlama seçeneği tarafından daha güçlü garantiler belirtilmediği sürece, @threads
makrosu tarafından yürütülen döngü aşağıdaki anlamlara sahiptir.
@threads
makrosu döngü gövdesini belirsiz bir sırada ve potansiyel olarak eşzamanlı olarak yürütür. Görevlerin ve işçi iş parçacıklarının kesin atamalarını belirtmez. Atamalar her yürütme için farklı olabilir. Her iterasyon için döngü gövdesi kodu (buna transitif olarak çağrılan herhangi bir kod da dahildir) iterasyonların görevlere dağıtımı veya bunların yürütüldüğü işçi iş parçacığı hakkında herhangi bir varsayımda bulunmamalıdır. Her iterasyon için döngü gövdesi, diğer iterasyonlardan bağımsız olarak ileriye doğru ilerleme yapabilmeli ve veri yarışlarından arınmış olmalıdır. Bu nedenle, iterasyonlar arasında geçersiz senkronizasyonlar kilitlenmelere neden olabilirken, senkronize edilmemiş bellek erişimleri tanımsız davranışa yol açabilir.
Örneğin, yukarıdaki koşullar şunları ima eder:
- Bir iterasyonda alınan bir kilit aynı iterasyonda serbest bırakılmalıdır.
Channel
gibi engelleyici ilkelere dayanarak iterasyonlar arasında iletişim kurmak yanlıştır.- Sadece iterasyonlar arasında paylaşılmayan konumlara yazılmalıdır (bir kilit veya atomik işlem kullanılmadıkça).
:static
planlaması kullanılmadıkça,threadid()
değeri tek bir iterasyon içinde bile değişebilir.Görev Göçü
bölümüne bakın.
Planlayıcılar
Planlayıcı argümanı olmadan, kesin planlama belirtilmemiştir ve Julia sürümleri arasında değişir. Şu anda, planlayıcı belirtilmediğinde :dynamic
kullanılmaktadır.
schedule
argümanı Julia 1.5 itibarıyla mevcuttur.
:dynamic
(varsayılan)
:dynamic
planlayıcısı, iterasyonları mevcut işçi iş parçacıklarına dinamik olarak yürütür. Mevcut uygulama, her iterasyon için iş yükünün eşit olduğunu varsayar. Ancak, bu varsayım gelecekte kaldırılabilir.
Bu planlama seçeneği, temel yürütme mekanizmasına yalnızca bir ipucudur. Ancak, birkaç özellik beklenebilir. :dynamic
planlayıcısı tarafından kullanılan Task
sayısı, mevcut işçi iş parçacıklarının sayısının küçük bir sabit katı ile sınırlıdır (Threads.threadpoolsize()
). Her görev, iterasyon alanının bitişik bölgelerini işler. Bu nedenle, @threads :dynamic for x in xs; f(x); end
genellikle @sync for x in xs; @spawn f(x); end
'den daha verimlidir, eğer length(xs)
işçi iş parçacıklarının sayısından önemli ölçüde daha büyükse ve f(x)
'in çalışma süresi, bir görevi başlatma ve senkronize etme maliyetinden (genellikle 10 mikro saniyeden az) nispeten daha küçükse.
schedule
argümanı için :dynamic
seçeneği mevcut ve varsayılan olarak Julia 1.8 itibarıyla mevcuttur.
:greedy
:greedy
planlayıcısı, her biri üretilen değerler üzerinde açgözlü bir şekilde çalışan Threads.threadpoolsize()
kadar görev başlatır. Bir görev işini bitirir bitirmez, iteratörden bir sonraki değeri alır. Herhangi bir bireysel görev tarafından yapılan iş, iteratörden bitişik değerler üzerinde olmayabilir. Verilen iteratör sonsuza kadar değer üretebilir, yalnızca iteratör arayüzü gereklidir (indeksleme yok).
Bu planlama seçeneği, bireysel iterasyonların iş yükü eşit değilse/büyük bir yayılma varsa genellikle iyi bir seçimdir.
schedule
argümanı için :greedy
seçeneği Julia 1.11 itibarıyla mevcuttur.
:static
:static
planlayıcısı, her iş parçacığı için bir görev oluşturur ve iterasyonları eşit olarak böler, her görevi belirli bir iş parçacığına atar. Özellikle, threadid()
değeri bir iterasyon içinde sabit olacağına dair garanti vardır. Başka bir @threads
döngüsünden veya 1 dışındaki bir iş parçacığından kullanıldığında :static
belirtmek bir hatadır.
:static
planlaması, Julia 1.3'ten önce yazılmış kodun geçişini desteklemek için mevcuttur. Yeni yazılmış kütüphane işlevlerinde, bu seçeneği kullanan işlevlerin rastgele işçi iş parçacıklarından çağrılamayacağı için :static
planlaması önerilmez.
Örnekler
Farklı planlama stratejilerini göstermek için, belirli bir süre boyunca çalışan bir zamanlayıcı döngüsü içeren busywait
fonksiyonunu düşünün.
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
örneği 2 saniye sürer çünkü boşta olan iş parçacıklarından biri, for döngüsünü tamamlamak için iki 1 saniyelik iterasyonu çalıştırabilir. ```
Base.Threads.foreach
— FunctionThreads.foreach(f, channel::Channel;
schedule::Threads.AbstractSchedule=Threads.FairSchedule(),
ntasks=Threads.threadpoolsize())
foreach(f, channel)
ile benzer, ancak channel
üzerinde yineleme ve f
'ye yapılan çağrılar, Threads.@spawn
tarafından başlatılan ntasks
görevine dağıtılır. Bu fonksiyon, tüm dahili olarak başlatılan görevlerin tamamlanmasını bekleyecektir.
Eğer schedule isa FairSchedule
ise, Threads.foreach
görevleri, Julia'nın zamanlayıcısının iş öğelerini daha serbest bir şekilde iş parçacıkları arasında yük dengelemesi yapabilmesi için başlatmaya çalışacaktır. Bu yaklaşım genellikle her bir öğe için daha yüksek bir yükleme süresine sahiptir, ancak diğer çok iş parçacıklı iş yükleri ile birlikte StaticSchedule
'dan daha iyi performans gösterebilir.
Eğer schedule isa StaticSchedule
ise, Threads.foreach
görevleri, FairSchedule
'dan daha düşük bir her bir öğe için yükleme süresi ile başlatacaktır, ancak yük dengelemesine daha az uygundur. Bu yaklaşım, ince taneli, homojen iş yükleri için daha uygun olabilir, ancak diğer çok iş parçacıklı iş yükleri ile birlikte FairSchedule
'dan daha kötü performans gösterebilir.
Örnekler
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]
Bu fonksiyon Julia 1.6 veya daha yenisini gerektirir.
Base.Threads.@spawn
— MacroThreads.@spawn [:default|:interactive] expr
Bir Task
oluşturur ve belirtilen thread havuzunda (belirtilmezse :default
) çalışması için schedule
eder. Görev, bir thread mevcut olduğunda ona tahsis edilir. Görevin bitmesini beklemek için, bu makronun sonucunda wait
çağrısı yapabilir veya fetch
çağrısı yaparak bekleyip dönen değerini alabilirsiniz.
Değerler, $
aracılığıyla @spawn
içine interpolasyon yapılabilir; bu, değeri doğrudan oluşturulan alt kapanıma kopyalar. Bu, bir değişkenin değerini eklemenizi sağlar ve asenkron kodu mevcut görevdeki değişkenin değerindeki değişikliklerden izole eder.
Görevin çalıştığı thread, görev yield ettiğinde değişebilir; bu nedenle threadid()
bir görev için sabit olarak değerlendirilmemelidir. Daha fazla önemli uyarılar için Task Migration
ve daha geniş çoklu iş parçacığı kılavuzuna bakın. Ayrıca thread havuzları bölümüne de bakın.
Bu makro, Julia 1.3 itibarıyla mevcuttur.
$
aracılığıyla değerlerin interpolasyonu, Julia 1.4 itibarıyla mevcuttur.
Bir thread havuzu, Julia 1.9 itibarıyla belirtilebilir.
Örnekler
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
Base.Threads.threadid
— FunctionThreads.threadid() -> Int
Geçerli yürütme ipliğinin kimlik numarasını alır. Ana iplik 1
kimliğine sahiptir.
Örnekler
julia> Threads.threadid()
1
julia> Threads.@threads for i in 1:4
println(Threads.threadid())
end
4
2
5
4
Bir görevin çalıştığı iplik, görev verirse değişebilir; bu duruma Görev Göçü
denir. Bu nedenle, çoğu durumda threadid()
'yi bir tampon veya durum nesneleri vektörüne indekslemek için kullanmak güvenli değildir.
Base.Threads.maxthreadid
— FunctionThreads.maxthreadid() -> Int
Julia işlemi için mevcut olan (tüm iş parçacığı havuzları arasında) iş parçacığı sayısının alt sınırını atomic-acquire semantiği ile alın. Sonuç, her zaman threadid()
ve threadid(task)
için gözlemleyebildiğiniz herhangi bir görev için büyük veya eşit olacaktır.
Base.Threads.nthreads
— FunctionThreads.nthreads(:default | :interactive) -> Int
Belirtilen iş parçacığı havuzundaki mevcut iş parçacığı sayısını alır. :interactive
içindeki iş parçacıkları 1:nthreads(:interactive)
kimlik numaralarına sahiptir ve :default
içindeki iş parçacıkları nthreads(:interactive) .+ (1:nthreads(:default))
kimlik numaralarına sahiptir.
Ayrıca LinearAlgebra
standart kütüphanesindeki BLAS.get_num_threads
ve BLAS.set_num_threads
ile Distributed
standart kütüphanesindeki nprocs()
ve Threads.maxthreadid()
ile de bakabilirsiniz.
Base.Threads.threadpool
— FunctionThreads.threadpool(tid = threadid()) -> Symbol
Belirtilen iş parçacığının iş parçacığı havuzunu döndürür; ya :default
, :interactive
veya :foreign
.
Base.Threads.nthreadpools
— FunctionThreads.nthreadpools() -> Int
Mevcut yapılandırılmış iş parçacığı havuzlarının sayısını döndürür.
Base.Threads.threadpoolsize
— FunctionThreads.threadpoolsize(pool::Symbol = :default) -> Int
Varsayılan iş parçacığı havuzuna (veya belirtilen iş parçacığı havuzuna) mevcut olan iş parçacığı sayısını alır.
Ayrıca bkz: BLAS.get_num_threads
ve BLAS.set_num_threads
LinearAlgebra
standart kütüphanesinde, ve nprocs()
Distributed
standart kütüphanesinde.
Base.Threads.ngcthreads
— FunctionThreads.ngcthreads() -> Int
Şu anda yapılandırılmış olan GC iş parçacıklarının sayısını döndürür. Bu, hem işaretleme iş parçacıklarını hem de eşzamanlı tarama iş parçacıklarını içerir.
Ayrıca Multi-Threading'e bakın.
Atomic operations
atomic
— KeywordUnsafe pointer işlemleri, C11 ve C++23'te sırasıyla _Atomic
ve std::atomic
türü ile tanımlanmış işaretçilerin yüklenmesi ve saklanması ile uyumludur. Julia türü T
için atomik yükleme desteği yoksa bir hata fırlatılabilir.
Ayrıca bakınız: unsafe_load
, unsafe_modify!
, unsafe_replace!
, unsafe_store!
, unsafe_swap!
Base.@atomic
— Macro@atomic var
@atomic order ex
var
veya ex
atomik olarak gerçekleştirilecektir, eğer ex
desteklenen bir ifade ise. Eğer order
belirtilmemişse, varsayılan olarak :sequentially_consistent kullanılır.
@atomic a.b.x = new
@atomic a.b.x += addend
@atomic :release a.b.x = new
@atomic :acquire_release a.b.x += addend
Sağda ifade edilen depolama işlemini atomik olarak gerçekleştir ve yeni değeri döndür.
=
ile bu işlem, setproperty!(a.b, :x, new)
çağrısına dönüşür. Herhangi bir operatör ile de bu işlem, modifyproperty!(a.b, :x, +, addend)[2]
çağrısına dönüşür.
@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
Sağda ifade edilen ikili işlemi atomik olarak gerçekleştir. Sonucu ilk argümandaki alana depola ve (eski, yeni)
değerlerini döndür.
Bu işlem, modifyproperty!(a.b, :x, func, arg2)
çağrısına dönüşür.
Daha fazla ayrıntı için kılavuzdaki Alan başına atomikler bölümüne bakın.
Örnekler
julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomic a.x # a'nın x alanını, sıralı tutarlılıkla al
1
julia> @atomic :sequentially_consistent a.x = 2 # a'nın x alanını, sıralı tutarlılıkla ayarla
2
julia> @atomic a.x += 1 # a'nın x alanını, sıralı tutarlılıkla artır
3
julia> @atomic a.x + 1 # a'nın x alanını, sıralı tutarlılıkla artır
3 => 4
julia> @atomic a.x # a'nın x alanını, sıralı tutarlılıkla al
4
julia> @atomic max(a.x, 10) # a'nın x alanını maksimum değere, sıralı tutarlılıkla değiştir
4 => 10
julia> @atomic a.x max 5 # yine a'nın x alanını maksimum değere, sıralı tutarlılıkla değiştir
10 => 10
Bu işlevsellik en az Julia 1.7'yi gerektirir.
```
Base.@atomicswap
— Macro@atomicswap a.b.x = new
@atomicswap :sequentially_consistent a.b.x = new
new
değerini a.b.x
'e kaydeder ve a.b.x
'in eski değerini döndürür.
Bu işlem, swapproperty!(a.b, :x, new)
çağrısına karşılık gelir.
Daha fazla ayrıntı için kılavuzdaki Alan bazında atomikler bölümüne bakın.
Örnekler
julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomicswap a.x = 2+2 # a'nın x alanını 4 ile değiştir, sıralı tutarlılıkla
1
julia> @atomic a.x # a'nın x alanını al, sıralı tutarlılıkla
4
Bu işlevsellik en az Julia 1.7'yi gerektirir.
Base.@atomicreplace
— Macro@atomicreplace a.b.x beklenen => istenen
@atomicreplace :sıralı_tutarlı a.b.x beklenen => istenen
@atomicreplace :sıralı_tutarlı :monotonik a.b.x beklenen => istenen
Koşullu değişimi atomik olarak gerçekleştirerek (eski, başarı::Bool)
değerlerini döndürün. Burada başarı
, değişimin tamamlanıp tamamlanmadığını gösterir.
Bu işlem, replaceproperty!(a.b, :x, beklenen, istenen)
çağrısına karşılık gelir.
Daha fazla bilgi için kılavuzdaki Alan başına atomikler bölümüne bakın.
Örnekler
julia> mutable struct Atomic{T}; @atomic x::T; end
julia> a = Atomic(1)
Atomic{Int64}(1)
julia> @atomicreplace a.x 1 => 2 # a'nın x alanını 1 ise 2 ile değiştir, sıralı tutarlılık ile
(eski = 1, başarı = true)
julia> @atomic a.x # a'nın x alanını al, sıralı tutarlılık ile
2
julia> @atomicreplace a.x 1 => 2 # a'nın x alanını 1 ise 2 ile değiştir, sıralı tutarlılık ile
(eski = 2, başarı = false)
julia> xchg = 2 => 0; # a'nın x alanını 2 ise 0 ile değiştir, sıralı tutarlılık ile
julia> @atomicreplace a.x xchg
(eski = 2, başarı = true)
julia> @atomic a.x # a'nın x alanını al, sıralı tutarlılık ile
0
Bu işlevsellik en az Julia 1.7'yi gerektirir.
Base.@atomiconce
— Macro@atomiconce a.b.x = value
@atomiconce :sequentially_consistent a.b.x = value
@atomiconce :sequentially_consistent :monotonic a.b.x = value
Değeri atomik olarak, daha önce ayarlanmamışsa koşullu olarak atayın ve `success::Bool` değerini döndürün. Burada `success`, atamanın tamamlanıp tamamlanmadığını gösterir.
Bu işlem, `setpropertyonce!(a.b, :x, value)` çağrısına karşılık gelir.
Daha fazla bilgi için kılavuzdaki [Alan başına atomikler](@ref man-atomics) bölümüne bakın.
# Örnekler
jldoctest julia> mutable struct AtomicOnce @atomic x AtomicOnce() = new() end
julia> a = AtomicOnce() AtomicOnce(#undef)
julia> @atomiconce a.x = 1 # a'nın x alanını 1 olarak ayarla, eğer ayarlanmamışsa, sıralı tutarlılıkla true
julia> @atomic a.x # a'nın x alanını al, sıralı tutarlılıkla 1
julia> @atomiconce a.x = 1 # a'nın x alanını 1 olarak ayarla, eğer ayarlanmamışsa, sıralı tutarlılıkla false
!!! compat "Julia 1.11"
Bu işlevsellik en az Julia 1.11 gerektirir.
Core.AtomicMemory
— TypeAtomicMemory{T} == GenericMemory{:atomic, T, Core.CPU}
Sabit boyutlu DenseVector{T}
. Elemanlarına erişim atomik olarak gerçekleştirilir (:monotonic
sıralaması ile). Elemanlardan herhangi birinin ayarlanması @atomic
makrosu kullanılarak ve sıralamanın açıkça belirtilmesiyle gerçekleştirilmelidir.
Her bir eleman erişildiğinde bağımsız olarak atomiktir ve atomik olmayan bir şekilde ayarlanamaz. Şu anda @atomic
makrosu ve daha yüksek seviyeli arayüz tamamlanmamıştır, ancak gelecekteki bir uygulama için yapı taşları içsel intrinziklerdir: Core.memoryrefget
, Core.memoryrefset!
, Core.memoryref_isassigned
, Core.memoryrefswap!
, Core.memoryrefmodify!
ve Core.memoryrefreplace!
.
Detaylar için Atomic Operations sayfasına bakın.
Bu tür, Julia 1.11 veya daha yenisini gerektirir.
unsafe
setindeki işlevler için, bu atomik işlemlerin C/C++ uyumlu sürümlerini seçen isteğe bağlı bellek sıralama parametreleri de vardır; eğer bu parametre unsafe_load
, unsafe_store!
, unsafe_swap!
, unsafe_replace!
ve unsafe_modify!
olarak belirtilirse.
Aşağıdaki API'ler kullanımdan kaldırılmıştır, ancak bunlar için destek muhtemelen birkaç sürüm boyunca devam edecektir.
Base.Threads.Atomic
— TypeThreads.Atomic{T}
Bir T
türünde bir nesneye referans tutar ve bunun yalnızca atomik olarak, yani bir iş parçacığına güvenli bir şekilde erişilmesini sağlar.
Yalnızca belirli "basit" türler atomik olarak kullanılabilir; bunlar, ilkel boolean, tam sayı ve kayan nokta türleridir. Bunlar Bool
, Int8
...Int128
, UInt8
...UInt128
ve Float16
...Float64
'tür.
Yeni atomik nesneler, atomik olmayan değerlerden oluşturulabilir; eğer belirtilmezse, atomik nesne sıfır ile başlatılır.
Atomik nesnelere []
notasyonu kullanılarak erişilebilir:
Örnekler
julia> x = Threads.Atomic{Int}(3)
Base.Threads.Atomic{Int64}(3)
julia> x[] = 1
1
julia> x[]
1
Atomik işlemler atomic_
ön eki kullanır, örneğin atomic_add!
, atomic_xchg!
vb.
Base.Threads.atomic_cas!
— FunctionThreads.atomic_cas!(x::Atomic{T}, cmp::T, newval::T) where T
Atomik olarak x
'i karşılaştır ve ayarla
x
'deki değeri cmp
ile atomik olarak karşılaştırır. Eğer eşitse, newval
'i x
'e yazar. Aksi takdirde, x
'i değiştirmeden bırakır. x
'deki eski değeri döner. Dönen değeri cmp
ile karşılaştırarak (===
aracılığıyla) x
'in değiştirilip değiştirilmediğini ve artık yeni değer newval
'i tutup tutmadığını bilirsiniz.
Daha fazla ayrıntı için, LLVM'nin cmpxchg
talimatına bakın.
Bu fonksiyon, işlemci semantiğini uygulamak için kullanılabilir. İşlemden önce, x
'deki değeri kaydedersiniz. İşlemden sonra, yeni değer yalnızca x
o sırada değiştirilmemişse saklanır.
Örnekler
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 T
x
içindeki değeri atomik olarak değiştirir.
x
içindeki değeri newval
ile atomik olarak değiştirir. Eski değeri döner.
Daha fazla ayrıntı için LLVM'nin atomicrmw xchg
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes
Atomik olarak val
değerini x
'e ekler.
x[] += val
işlemini atomik olarak gerçekleştirir. Eski değeri döner. Atomic{Bool}
için tanımlı değildir.
Daha fazla ayrıntı için LLVM'nin atomicrmw add
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes
val
'ı x
'ten atomik olarak çıkarır.
x[] -= val
işlemini atomik olarak gerçekleştirir. Eski değeri döner. Atomic{Bool}
için tanımlı değildir.
Daha fazla ayrıntı için LLVM'nin atomicrmw sub
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_and!(x::Atomic{T}, val::T) where T
Atomik olarak x
ile val
'i bit düzeyinde ve işlemi yapar.
x[] &= val
işlemini atomik olarak gerçekleştirir. Eski değeri döner.
Daha fazla ayrıntı için, LLVM'nin atomicrmw and
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_nand!(x::Atomic{T}, val::T) where T
Atomik olarak x
'i val
ile bitwise-nand (not-and) yapar.
x[] = ~(x[] & val)
işlemini atomik olarak gerçekleştirir. Eski değeri döner.
Daha fazla ayrıntı için, LLVM'nin atomicrmw nand
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_or!(x::Atomic{T}, val::T) where T
x
ile val
'ı atomik olarak bitwise-or yapar.
x[] |= val
işlemini atomik olarak gerçekleştirir. Eski değeri döner.
Daha fazla ayrıntı için LLVM'nin atomicrmw or
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_xor!(x::Atomic{T}, val::T) where T
Atomik olarak x
'i val
ile bit düzeyinde XOR (özel veya) işlemi yapar.
x[] $= val
işlemini atomik olarak gerçekleştirir. Eski değeri döner.
Daha fazla ayrıntı için LLVM'nin atomicrmw xor
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_max!(x::Atomic{T}, val::T) where T
x
ve val
'in maksimumunu atomik olarak x
'e kaydedin
Atomik olarak x[] = max(x[], val)
işlemini gerçekleştirir. Eski değeri döndürür.
Daha fazla ayrıntı için, LLVM'nin atomicrmw max
talimatına bakın.
Örnekler
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!
— FunctionThreads.atomic_min!(x::Atomic{T}, val::T) where T
x
ve val
'in minimumunu atomik olarak x
'e kaydedin
Atomik olarak x[] = min(x[], val)
işlemini gerçekleştirir. Eski değeri döndürür.
Daha fazla ayrıntı için, LLVM'nin atomicrmw min
talimatına bakın.
Örnekler
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
— FunctionThreads.atomic_fence()
Sıralı tutarlılık bellek engeli ekleyin
Sıralı tutarlılık sıralama anlamsalara sahip bir bellek engeli ekler. Bunun gerekli olduğu, yani bir edinme/salım sıralamasının yetersiz olduğu algoritmalar vardır.
Bu muhtemelen çok pahalı bir işlemdir. Julia'daki diğer tüm atomik işlemlerin zaten edinme/salım anlamsalara sahip olduğu göz önüne alındığında, açık engeller çoğu durumda gerekli olmamalıdır.
Daha fazla ayrıntı için, LLVM'nin fence
talimatına bakın.
ccall using a libuv threadpool (Experimental)
Base.@threadcall
— Macro@threadcall((cfunc, clib), rettype, (argtypes...), argvals...)
@threadcall
makrosu, ccall
ile aynı şekilde çağrılır, ancak işi farklı bir iş parçacığında yapar. Bu, mevcut julia
iş parçacığının engellenmeden bir engelleyici C fonksiyonunu çağırmak istediğinizde yararlıdır. Eşzamanlılık, libuv iş parçacığı havuzunun boyutuyla sınırlıdır; varsayılan olarak 4 iş parçacığıdır, ancak UV_THREADPOOL_SIZE
ortam değişkenini ayarlayarak ve julia
sürecini yeniden başlatarak artırılabilir.
Çağrılan fonksiyonun asla Julia'ya geri çağrıda bulunmaması gerektiğini unutmayın.
Low-level synchronization primitives
Bu yapı taşları, düzenli senkronizasyon nesnelerini oluşturmak için kullanılır.
Base.Threads.SpinLock
— TypeSpinLock()
Reentransız, test-et-test-et-ve-kur kilidi oluşturun. Rekürsif kullanım bir ölü kilitlenmeye yol açacaktır. Bu tür bir kilit, yalnızca kısa sürede çalıştırılan ve engellemeyen (örneğin, I/O gerçekleştiren) kod etrafında kullanılmalıdır. Genel olarak, ReentrantLock
bunun yerine kullanılmalıdır.
Her lock
bir unlock
ile eşleştirilmelidir. Eğer !islocked(lck::SpinLock)
geçerliyse, trylock(lck)
başarılı olur, aksi takdirde kilidi "aynı anda" tutmaya çalışan başka görevler varsa başarısız olur.
Test-et-test-et-ve-kur spin kilitleri, yaklaşık 30 civarında rekabet eden iş parçacığına kadar en hızlıdır. Eğer bundan daha fazla rekabet varsa, farklı senkronizasyon yaklaşımları düşünülmelidir.