Tasks
Core.Task
— TypeGörev(func)
Verilen func
fonksiyonunu (argüman almayan bir çağrılabilir olmalıdır) yürütmek için bir Görev
(yani coroutine) oluşturun. Görev, bu fonksiyon döndüğünde sona erer. Görev, schedule
ile yapılandırıldığında, inşaat sırasında ebeveynin "dünya yaşı" içinde çalışacaktır.
!!! uyarı Varsayılan olarak görevlerin yapışkan bitinin t.sticky
olarak ayarlandığına dikkat edin. Bu, @async
için tarihsel varsayılanı modellemektedir. Yapışkan görevler yalnızca ilk olarak planlandıkları işçi iş parçacığında çalıştırılabilir ve planlandıklarında, planlandıkları görev yapışkan hale gelecektir. Threads.@spawn
davranışını elde etmek için yapışkan bitini manuel olarak false
olarak ayarlayın.
Örnekler
julia> a() = sum(i for i in 1:1000);
julia> b = Task(a);
Bu örnekte, b
henüz başlamamış bir çalıştırılabilir Görev
dir.
Base.@task
— Macro@task
Bir ifadeyi çalıştırmadan Task
içine sarın ve Task
döndürün. Bu yalnızca bir görev oluşturur ve çalıştırmaz.
Varsayılan olarak görevlerin yapışkan bitinin true
olarak ayarlanmış olacaktır t.sticky
. Bu, @async
için tarihsel varsayılanı modellemektedir. Yapışkan görevler yalnızca ilk olarak planlandıkları işçi iş parçacığında çalıştırılabilir ve planlandıklarında, planlandıkları görev yapışkan hale gelecektir. Threads.@spawn
davranışını elde etmek için yapışkan bitini manuel olarak false
olarak ayarlayın.
Örnekler
julia> a1() = sum(i for i in 1:1000);
julia> b = @task a1();
julia> istaskstarted(b)
false
julia> schedule(b);
julia> yield();
julia> istaskdone(b)
true
Base.@async
— Macro@async
Bir ifadeyi Task
içine sarın ve yerel makinenin zamanlayıcı kuyruğuna ekleyin.
Değerler, $
aracılığıyla @async
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.
@async
yerine her zaman Threads.@spawn
kullanmanız şiddetle tavsiye edilir paralellik gereksinimi olmasa bile, özellikle kamuya açık dağıtılan kütüphanelerde. Bunun nedeni, @async
kullanımının, Julia'nın mevcut uygulamasında ebeveyn görevlerin işçi iş parçacıkları arasında taşınmasını devre dışı bırakmasıdır. Bu nedenle, bir kütüphane fonksiyonunda @async
kullanımının görünüşte masum bir şekilde, kullanıcı uygulamalarının çok farklı bölümlerinin performansı üzerinde büyük bir etkisi olabilir.
$
aracılığıyla değerlerin interpolasyonu, Julia 1.4 itibarıyla mevcuttur.
Base.asyncmap
— Functionasyncmap(f, c...; ntasks=0, batch_size=nothing)
Bir koleksiyon (veya birden fazla eşit uzunlukta koleksiyon) üzerinde f
'yi eşzamanlı görevler kullanarak haritalar. Birden fazla koleksiyon argümanı için, f
eleman bazında uygulanır.
ntasks
, eşzamanlı olarak çalıştırılacak görev sayısını belirtir. Koleksiyonların uzunluğuna bağlı olarak, ntasks
belirtilmemişse, eşzamanlı haritalama için en fazla 100 görev kullanılacaktır.
ntasks
, ayrıca sıfır argümanlı bir fonksiyon olarak da belirtilebilir. Bu durumda, her bir elemanı işlemden önce paralel olarak çalıştırılacak görev sayısı kontrol edilir ve ntasks_func
değeri mevcut görev sayısından büyükse yeni bir görev başlatılır.
batch_size
belirtilirse, koleksiyon toplu modda işlenir. Bu durumda, f
, bir Vector
argüman demetleri kabul eden ve bir sonuç vektörü döndüren bir fonksiyon olmalıdır. Girdi vektörünün uzunluğu batch_size
veya daha az olacaktır.
Aşağıdaki örnekler, haritalama fonksiyonunun çalıştırıldığı görevlerin objectid
'sini döndürerek farklı görevlerdeki yürütmeyi vurgular.
Öncelikle, ntasks
tanımlanmamışken, her bir eleman farklı bir görevde işlenir.
julia> tskoid() = objectid(current_task());
julia> asyncmap(x->tskoid(), 1:5)
5-element Array{UInt64,1}:
0x6e15e66c75c75853
0x440f8819a1baa682
0x9fb3eeadd0c83985
0xebd3e35fe90d4050
0x29efc93edce2b961
julia> length(unique(asyncmap(x->tskoid(), 1:5)))
5
ntasks=2
ile tüm elemanlar 2 görevde işlenir.
julia> asyncmap(x->tskoid(), 1:5; ntasks=2)
5-element Array{UInt64,1}:
0x027ab1680df7ae94
0xa23d2f80cd7cf157
0x027ab1680df7ae94
0xa23d2f80cd7cf157
0x027ab1680df7ae94
julia> length(unique(asyncmap(x->tskoid(), 1:5; ntasks=2)))
2
batch_size
tanımlandığında, haritalama fonksiyonu bir argüman demetleri dizisini kabul edecek ve bir sonuç dizisi döndürecek şekilde değiştirilmelidir. Bunu başarmak için değiştirilmiş haritalama fonksiyonunda map
kullanılır.
julia> batch_func(input) = map(x->string("args_tuple: ", x, ", element_val: ", x[1], ", task: ", tskoid()), input)
batch_func (generic function with 1 method)
julia> asyncmap(batch_func, 1:5; ntasks=2, batch_size=2)
5-element Array{String,1}:
"args_tuple: (1,), element_val: 1, task: 9118321258196414413"
"args_tuple: (2,), element_val: 2, task: 4904288162898683522"
"args_tuple: (3,), element_val: 3, task: 9118321258196414413"
"args_tuple: (4,), element_val: 4, task: 4904288162898683522"
"args_tuple: (5,), element_val: 5, task: 9118321258196414413"
Base.asyncmap!
— Functionasyncmap!(f, results, c...; ntasks=0, batch_size=nothing)
asyncmap
gibi, ancak çıktıyı bir koleksiyon döndürmek yerine results
içinde saklar.
!!! uyarı Herhangi bir değiştirilmiş argümanın başka bir argümanla bellek paylaşması durumunda davranış beklenmedik olabilir.
Base.current_task
— Functioncurrent_task()
Şu anda çalışan Task
nesnesini alır.
Base.istaskdone
— Functionistaskdone(t::Task) -> Bool
Bir görevin sona erip ermediğini belirleyin.
Örnekler
julia> a2() = sum(i for i in 1:1000);
julia> b = Task(a2);
julia> istaskdone(b)
false
julia> schedule(b);
julia> yield();
julia> istaskdone(b)
true
Base.istaskstarted
— Functionistaskstarted(t::Task) -> Bool
Bir görevin çalışmaya başlayıp başlamadığını belirleyin.
Örnekler
julia> a3() = sum(i for i in 1:1000);
julia> b = Task(a3);
julia> istaskstarted(b)
false
Base.istaskfailed
— Functionistaskfailed(t::Task) -> Bool
Bir görevin bir istisna atıldığı için çıkıp çıkmadığını belirleyin.
Örnekler
julia> a4() = error("görev başarısız");
julia> b = Task(a4);
julia> istaskfailed(b)
false
julia> schedule(b);
julia> yield();
julia> istaskfailed(b)
true
Bu fonksiyon en az Julia 1.3 gerektirir.
Base.task_local_storage
— Methodtask_local_storage(key)
Mevcut görevin görev-yerel depolamasında bir anahtarın değerini kontrol edin.
Base.task_local_storage
— Methodtask_local_storage(key, value)
Mevcut görevin görev yerel depolamasında bir anahtara bir değer atar.
Base.task_local_storage
— Methodtask_local_storage(body, key, value)
value
'ı key
'e atayarak değiştirilmiş görev-yerel depolama ile body
fonksiyonunu çağırır; key
'in önceki değeri veya yokluğu daha sonra geri yüklenir. Dinamik kapsamı taklit etmek için yararlıdır.
Scheduling
Base.yield
— Functionyield()
Başka bir planlanmış görevin çalışmasına izin vermek için zamanlayıcıya geçin. Bu işlevi çağıran bir görev hala çalıştırılabilir durumdadır ve başka çalıştırılabilir görev yoksa hemen yeniden başlatılacaktır.
yield(t::Task, arg = nothing)
schedule(t, arg); yield()
'in hızlı, adaletsiz zamanlama versiyonu olan bu fonksiyon, zamanlayıcıyı çağırmadan önce hemen t
'ye geçiş yapar.
Base.yieldto
— Functionyieldto(t::Task, arg = nothing)
Verilen göreve geçiş yapar. Bir göreve ilk kez geçildiğinde, görevin fonksiyonu hiçbir argüman olmadan çağrılır. Sonraki geçişlerde, arg
son yieldto
çağrısından döner. Bu, yalnızca görevleri değiştiren, durumları veya zamanlamayı dikkate almayan düşük seviyeli bir çağrıdır. Kullanımı önerilmez.
Base.sleep
— Functionsleep(seconds)
Mevcut görevi belirtilen sayıda saniye boyunca engeller. Minimum uyku süresi 1 milisaniye veya 0.001
girişi kadardır.
Base.schedule
— Functionschedule(t::Task, [val]; error=false)
Bir Task
zamanlayıcının kuyruğuna eklenir. Bu, sistem başka bir şey yapmadığında görevin sürekli çalışmasını sağlar, ancak görev wait
gibi bir engelleyici işlem yapıyorsa bu durum geçerli değildir.
İkinci bir argüman val
sağlanırsa, bu değer görev tekrar çalıştığında (yieldto
aracılığıyla) göreve iletilecektir. Eğer error
true
ise, değer uyanan görevde bir istisna olarak yükseltilir.
!!! uyarı Zaten başlatılmış bir Task
üzerinde schedule
kullanmak yanlıştır. Daha fazla bilgi için API referansına bakın.
!!! uyarı Varsayılan olarak görevlerin yapışkan bitinin true
olarak ayarlandığı t.sticky
olacaktır. Bu, @async
için tarihsel varsayılanı modellemektedir. Yapışkan görevler yalnızca ilk olarak planlandıkları işçi iş parçacığında çalıştırılabilir ve planlandıklarında, planlandıkları görev yapışkan hale gelecektir. Threads.@spawn
davranışını elde etmek için yapışkan bitini manuel olarak false
olarak ayarlayın.
Örnekler
julia> a5() = sum(i for i in 1:1000);
julia> b = Task(a5);
julia> istaskstarted(b)
false
julia> schedule(b);
julia> yield();
julia> istaskstarted(b)
true
julia> istaskdone(b)
true
Synchronization
Base.errormonitor
— Functionerrormonitor(t::Task)
Görev t
başarısız olursa stderr
'ye bir hata günlüğü yazdırır.
Örnekler
julia> Base._wait(errormonitor(Threads.@spawn error("görev başarısız oldu")))
Unhandled Task ERROR: görev başarısız oldu
Stacktrace:
[...]
Base.@sync
— Macro@sync
Tüm sözdizimsel olarak kapsanan @async
, @spawn
, Distributed.@spawnat
ve Distributed.@distributed
kullanımlarının tamamlanmasını bekleyin. Kapsanan asenkron işlemler tarafından fırlatılan tüm istisnalar toplanır ve bir CompositeException
olarak fırlatılır.
Örnekler
julia> Threads.nthreads()
4
julia> @sync begin
Threads.@spawn println("Thread-id $(Threads.threadid()), task 1")
Threads.@spawn println("Thread-id $(Threads.threadid()), task 2")
end;
Thread-id 3, task 1
Thread-id 1, task 2
Base.wait
— FunctionThreads.Condition
için özel not:
Çağrıcı, bu yöntemi çağırmadan önce bir Threads.Condition
'a sahip olan lock
tutmalıdır. Çağrılan görev, genellikle aynı Threads.Condition
nesnesi üzerinde notify
çağrılarak başka bir görev tarafından uyandırılana kadar engellenecektir. Engelleme sırasında kilit atomik olarak serbest bırakılacak (kendi kendine kilitlenmiş olsa bile) ve geri dönerken yeniden edinilecektir.
wait(r::Future)
Belirtilen Future
için bir değerin mevcut olmasını bekleyin.
wait(r::RemoteChannel, args...)
Belirtilen RemoteChannel
üzerinde bir değerin kullanılabilir hale gelmesini bekleyin.
wait([x])
Mevcut görevi, argümanın türüne bağlı olarak bir olay meydana gelene kadar engeller:
Channel
: Kanalda bir değerin eklenmesini bekleyin.Condition
: Bir koşuldanotify
için bekleyin venotify
'ye geçirilenval
parametresini döndürün. Bir koşulda beklemek, ayrıcafirst=true
geçişine izin verir; bu, bekleyeninnotify
üzerinde uyanmak için sıranın ilk sırasına yerleştirilmesiyle sonuçlanır, bu da genellikle ilk giren ilk çıkar davranışıdır.Process
: Bir işlemin veya işlem zincirinin çıkmasını bekleyin. Bir işleminexitcode
alanı, başarı veya başarısızlığı belirlemek için kullanılabilir.Task
: BirTask
'ın bitmesini bekleyin. Eğer görev bir istisna ile başarısız olursa, birTaskFailedException
(başarısız olan görevi saran) fırlatılır.RawFD
: Bir dosya tanımlayıcısındaki değişiklikleri bekleyin (bkz.FileWatching
paketi).
Hiçbir argüman geçilmezse, görev belirsiz bir süre engellenir. Bir görev yalnızca schedule
veya yieldto
için açık bir çağrı ile yeniden başlatılabilir.
Genellikle wait
, beklenen bir koşulun karşılandığından emin olmak için bir while
döngüsü içinde çağrılır.
wait(c::Channel)
Channel
isready
olana kadar engeller.
julia> c = Channel(1);
julia> isready(c)
false
julia> task = Task(() -> wait(c));
julia> schedule(task);
julia> istaskdone(task) # görev engellendi çünkü kanal hazır değil
false
julia> put!(c, 1);
julia> istaskdone(task) # görev şimdi engellenmedi
true
Base.fetch
— Methodfetch(t::Task)
Bir Task
tamamlanmasını bekleyin, ardından sonuç değerini döndürün. Görev bir istisna ile başarısız olursa, başarısız görevi saran bir TaskFailedException
fırlatılır.
Base.fetch
— Methodfetch(x::Any)
x
değerini döndür.
Base.timedwait
— Functiontimedwait(testcb, timeout::Gerçek; pollint::Gerçek=0.1)
testcb()
true
döndürene kadar veya timeout
saniye geçene kadar bekleyin, hangisi daha önce olursa. Test fonksiyonu her pollint
saniyede bir kontrol edilir. pollint
için minimum değer 0.001 saniyedir, yani 1 milisaniye.
:ok
veya :timed_out
döndürün.
Örnekler
julia> cb() = (sleep(5); return);
julia> t = @async cb();
julia> timedwait(()->istaskdone(t), 1)
:timed_out
julia> timedwait(()->istaskdone(t), 6.5)
:ok
Base.Condition
— TypeCondition()
Görevlerin bekleyebileceği bir kenar tetiklemeli olay kaynağı oluşturur. Condition
üzerinde wait
çağrısı yapan görevler askıya alınır ve sıraya alınır. Daha sonra Condition
üzerinde notify
çağrısı yapıldığında görevler uyandırılır. Bir koşulda beklemek, bir değer döndürebilir veya notify
'nin isteğe bağlı argümanları kullanıldığında bir hata oluşturabilir. Kenar tetikleme, yalnızca notify
çağrıldığında bekleyen görevlerin uyandırılabileceği anlamına gelir. Seviye tetiklemeli bildirimler için, bir bildirimin olup olmadığını takip etmek için ekstra bir durum tutmalısınız. Channel
ve Threads.Event
türleri bunu yapar ve seviye tetiklemeli olaylar için kullanılabilir.
Bu nesne THREAD-GÜVENLİ değildir. Thread-güvenli bir versiyon için Threads.Condition
'e bakın.
Base.Threads.Condition
— TypeThreads.Condition([lock])
Base.Condition
için thread güvenli bir versiyon.
Bir Threads.Condition
üzerinde wait
veya notify
çağırmak için önce ona lock
çağırmalısınız. wait
çağrıldığında, kilit atomik olarak bloklama sırasında serbest bırakılır ve wait
döndüğünde yeniden edinilecektir. Bu nedenle, bir Threads.Condition
c
kullanımı aşağıdaki gibi görünmelidir:
lock(c)
try
while !thing_we_are_waiting_for
wait(c)
end
finally
unlock(c)
end
Bu işlevsellik en az Julia 1.2'yi gerektirir.
Base.Event
— TypeEvent([autoreset=false])
Seviye tetikleyici bir olay kaynağı oluşturur. wait
çağrısı yapan görevler, Event
üzerinde notify
çağrısı yapılana kadar askıya alınır ve sıraya alınır. notify
çağrısı yapıldıktan sonra, Event
bir sinyal durumunda kalır ve görevler artık beklerken engellenmez, ta ki reset
çağrısı yapılana kadar.
Eğer autoreset
doğruysa, her notify
çağrısı için en fazla bir görev wait
'ten serbest bırakılacaktır.
Bu, notify/wait üzerinde bir edinme ve serbest bırakma bellek sıralaması sağlar.
Bu işlevsellik en az Julia 1.1 gerektirir.
autoreset
işlevselliği ve bellek sıralama garantisi en az Julia 1.8 gerektirir.
Base.notify
— Functionnotify(condition, val=nothing; all=true, error=false)
Bir koşulu bekleyen görevleri uyandırın, onlara val
geçirin. Eğer all
true
ise (varsayılan), bekleyen tüm görevler uyandırılır, aksi takdirde yalnızca biri uyandırılır. Eğer error
true
ise, geçirilen değer uyandırılan görevlerde bir istisna olarak yükseltilir.
Uyandırılan görevlerin sayısını döndürün. Eğer condition
üzerinde bekleyen görev yoksa 0 döndürün.
Base.reset
— Methodreset(::Event)
Bir Event
nesnesini ayarlanmamış bir duruma geri döndürür. Daha sonra wait
çağrıları, notify
tekrar çağrılana kadar engellenecektir.
Base.Semaphore
— TypeSemaphore(sem_size)
En fazla sem_size
edinimin kullanılmasına izin veren bir sayım semaforu oluşturur. Her edinim, bir serbest bırakma ile eşleşmelidir.
Bu, edinim/serbest bırakma çağrıları üzerinde bir edinim ve serbest bırakma bellek sıralaması sağlar.
Base.acquire
— Functionacquire(s::Semaphore)
Bir sem_size
izninin mevcut olmasını bekleyin, birinin alınmasını engelleyerek bekleyin.
acquire(f, s::Semaphore)
Semaphore s
'den alım yaptıktan sonra f
'yi çalıştırır ve tamamlandığında veya hata oluştuğunda release
eder.
Örneğin, aynı anda yalnızca 2 foo
çağrısının aktif olmasını sağlayan bir do-block biçimi:
s = Base.Semaphore(2)
@sync for _ in 1:100
Threads.@spawn begin
Base.acquire(s) do
foo()
end
end
end
Bu yöntem en az Julia 1.8 gerektirir.
Base.release
— Functionrelease(s::Semaphore)
Havuzdan bir izin geri verin, bu da başka bir görevin onu almasına ve yürütmeye devam etmesine olanak tanıyabilir.
Base.AbstractLock
— TypeAbstractLock
Senkranma ilkelilerini uygulayan türleri tanımlayan soyut süper tür: lock
, trylock
, unlock
ve islocked
.
Base.lock
— Functionlock(lock)
lock
mevcut olduğunda onu edin. Eğer lock
başka bir görev/işlem tarafından zaten kilitlenmişse, mevcut hale gelmesini bekleyin.
Her lock
, bir unlock
ile eşleşmelidir.
lock(f::Function, lock)
lock
'ı al, lock
tutulurken f
'yi çalıştır ve f
döndüğünde lock
'ı serbest bırak. Eğer lock
başka bir görev/iş parçacığı tarafından zaten kilitlenmişse, kullanılabilir hale gelene kadar bekle.
Bu fonksiyon döndüğünde, lock
serbest bırakılmıştır, bu yüzden çağıran kişi unlock
yapmaya çalışmamalıdır.
Ayrıca bakınız: @lock
.
İkinci argüman olarak bir Channel
kullanmak, Julia 1.7 veya daha yenisini gerektirir.
lock(f::Function, l::Lockable)
l
ile ilişkili kilidi al, f
'yi kilitli olarak çalıştır ve f
döndüğünde kilidi serbest bırak. f
, l
tarafından sarılmış olan değeri alan bir konumsal argüman alacaktır. Eğer kilit başka bir görev/işlem tarafından zaten kilitlenmişse, kullanılabilir hale gelene kadar bekle. Bu fonksiyon döndüğünde, lock
serbest bırakılmıştır, bu nedenle çağıran kişi onu unlock
etmeye çalışmamalıdır.
En az Julia 1.11 gerektirir.
Base.unlock
— Functionunlock(lock)
lock
'ın sahipliğini serbest bırakır.
Eğer bu daha önce edinilmiş bir rekürsif kilit ise, dahili bir sayacı azaltır ve hemen döner.
Base.trylock
— Functiontrylock(lock) -> Başarı (Boolean)
Kilidi mevcutsa alır ve başarılıysa true
döner. Eğer kilit başka bir görev/iş parçacığı tarafından zaten kilitlenmişse, false
döner.
Her başarılı trylock
, bir unlock
ile eşleşmelidir.
trylock
fonksiyonu, islocked
ile birleştirildiğinde, eğer typeof(lock)
tarafından destekleniyorsa test-et-test-et-ve-set veya üssel geri çekilme algoritmalarını yazmak için kullanılabilir (belgesini okuyun).
Base.islocked
— Functionislocked(lock) -> Durum (Boolean)
lock
'ın herhangi bir görev/iş parçacığı tarafından tutulup tutulmadığını kontrol eder. Bu fonksiyon tek başına senkronizasyon için kullanılmamalıdır. Ancak, islocked
ile trylock
birleştirildiğinde, test-et-test-et-ve-set veya üssel geri çekilme algoritmalarını yazmak için kullanılabilir eğer typeof(lock)
tarafından destekleniyorsa (belgelerini okuyun).
Genişletilmiş yardım
Örneğin, lock
uygulaması aşağıda belgelenen özellikleri karşıladıysa, üssel geri çekilme şu şekilde uygulanabilir.
nspins = 0
while true
while islocked(lock)
GC.safepoint()
nspins += 1
nspins > LIMIT && error("timeout")
end
trylock(lock) && break
backoff()
end
Uygulama
Bir kilit uygulamasının islocked
'ı aşağıdaki özelliklerle tanımlaması ve bunu belgelerinde belirtmesi önerilir.
islocked(lock)
veri yarışı içermemelidir.- Eğer
islocked(lock)
false
dönerse, diğer görevlerden herhangi bir müdahale yoksatrylock(lock)
'ın hemen çağrılması başarılı olmalıdır (true
döner).
Base.ReentrantLock
— TypeReentrantLock()
Bir Task
senkronizasyonu için yeniden giriş kilidi oluşturur. Aynı görev, gerektiği kadar kilidi alabilir (bu, adın "Yeniden Giriş" kısmının anlamıdır). Her lock
bir unlock
ile eşleşmelidir.
lock
çağrısı, ilgili unlock
'a kadar o iş parçacığında sonlandırıcıların çalışmasını da engelleyecektir. Aşağıda gösterilen standart kilit deseninin doğal olarak desteklenmesi gerekir, ancak deneme/kilit sırasını tersine çevirmekten veya deneme bloğunu tamamen atlamaktan (örneğin, kilit hala tutulurken geri dönmeye çalışmak) kaçının:
Bu, kilit/açma çağrıları üzerinde bir alma/serbest bırakma bellek sıralaması sağlar.
lock(l)
try
<atomik iş>
finally
unlock(l)
end
Eğer !islocked(lck::ReentrantLock)
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.
Base.@lock
— Macro@lock l expr
lock(f, l::AbstractLock)
makrosunun f
fonksiyonu yerine expr
ile versiyonudur. Aşağıdakine genişler:
lock(l)
try
expr
finally
unlock(l)
end
Bu, lock
kullanarak bir do
bloğu ile benzer, ancak bir kapanış oluşturmaktan kaçınır ve böylece performansı artırabilir.
@lock
, Julia 1.3'te eklendi ve Julia 1.10'da dışa aktarıldı.
Base.Lockable
— TypeLockable(value, lock = ReentrantLock())
value
ile birlikte sağlanan lock
ile ilişkilendirilmiş bir Lockable
nesnesi oluşturur. Bu nesne @lock
, lock
, trylock
, unlock
destekler. Değere erişmek için, kilidi tutarken kilitlenebilir nesneyi indeksleyin.
En az Julia 1.11 gerektirir.
Örnek
julia> locked_list = Base.Lockable(Int[]);
julia> @lock(locked_list, push!(locked_list[], 1)) # değere erişmek için kilidi tutmalısınız
1-element Vector{Int64}:
1
julia> lock(summary, locked_list)
"1-element Vector{Int64}"
Channels
Base.AbstractChannel
— TypeAbstractChannel{T}
T
türündeki nesneleri ileten bir kanalın temsili.
Base.Channel
— TypeChannel{T=Any}(size::Int=0)
T
türünde size
nesnesini tutabilen bir Channel
oluşturur. Dolu bir kanalda put!
çağrıları, bir nesne take!
ile çıkarılana kadar bloklanır.
Channel(0)
tamponu olmayan bir kanal oluşturur. put!
, eşleşen bir take!
çağrılana kadar bloklanır. Ve tersine.
Diğer yapıcılar:
Channel()
: varsayılan yapıcı,Channel{Any}(0)
ile eşdeğerdirChannel(Inf)
:Channel{Any}(typemax(Int))
ile eşdeğerdirChannel(sz)
:Channel{Any}(sz)
ile eşdeğerdir
Varsayılan yapıcı Channel()
ve varsayılan size=0
, Julia 1.3'te eklendi.
Base.Channel
— MethodChannel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing)
func
'dan yeni bir görev oluşturun, bunu T
türünde ve size
boyutunda yeni bir kanala bağlayın ve görevi, hepsini tek bir çağrıda planlayın. Görev sona erdiğinde kanal otomatik olarak kapatılır.
func
, bağlı kanalı tek argümanı olarak kabul etmelidir.
Oluşturulan göreve bir referansa ihtiyacınız varsa, anahtar argüman taskref
aracılığıyla bir Ref{Task}
nesnesi geçirin.
Eğer spawn=true
ise, func
için oluşturulan Task
, paralel olarak başka bir iş parçacığında planlanabilir; bu, bir görevi Threads.@spawn
aracılığıyla oluşturmakla eşdeğerdir.
Eğer spawn=true
ve threadpool
argümanı ayarlanmamışsa, varsayılan olarak :default
olur.
Eğer threadpool
argmanı ayarlanmışsa (:default
veya :interactive
olarak), bu spawn=true
anlamına gelir ve yeni Görev belirtilen iş parçacığı havuzuna oluşturulur.
Bir Channel
döndürün.
Örnekler
julia> chnl = Channel() do ch
foreach(i -> put!(ch, i), 1:4)
end;
julia> typeof(chnl)
Channel{Any}
julia> for i in chnl
@show i
end;
i = 1
i = 2
i = 3
i = 4
Oluşturulan görevi referans alma:
julia> taskref = Ref{Task}();
julia> chnl = Channel(taskref=taskref) do ch
println(take!(ch))
end;
julia> istaskdone(taskref[])
false
julia> put!(chnl, "Hello");
Hello
julia> istaskdone(taskref[])
true
spawn=
parametresi Julia 1.3'te eklendi. Bu yapıcı Julia 1.3'te eklendi. Daha önceki Julia sürümlerinde, Channel size
ve T
'yi ayarlamak için anahtar argümanlar kullanıyordu, ancak bu yapıcılar kullanımdan kaldırılmıştır.
threadpool=
argümanı Julia 1.9'da eklendi.
julia> chnl = Channel{Char}(1, spawn=true) do ch
for c in "hello world"
put!(ch, c)
end
end
Channel{Char}(1) (2 items available)
julia> String(collect(chnl))
"hello world"
Base.put!
— Methodput!(c::Channel, v)
Kanal c
'ye bir öğe v
ekler. Kanal doluysa engeller.
Tamponlanmamış kanallar için, farklı bir görev tarafından take!
gerçekleştirilene kadar engeller.
v
, put!
çağrıldığında kanalın türüne convert
ile dönüştürülür.
Base.take!
— Methodtake!(c::Channel)
Bir Channel
içinden sırayla bir değeri kaldırır ve döndürür. Veri mevcut olana kadar engeller. Tamponlanmamış kanallar için, başka bir görev tarafından bir put!
gerçekleştirilene kadar engeller.
Örnekler
Tamponlu kanal:
julia> c = Channel(1);
julia> put!(c, 1);
julia> take!(c)
1
Tamponsuz kanal:
julia> c = Channel(0);
julia> task = Task(() -> put!(c, 1));
julia> schedule(task);
julia> take!(c)
1
Base.isready
— Methodisready(c::Channel)
Bir Channel
içinde bir değer olup olmadığını belirler. Hemen döner, engellemez.
Tamponlanmamış kanallar için, put!
üzerinde bekleyen görevler varsa true
döner.
Örnekler
Tamponlu kanal:
julia> c = Channel(1);
julia> isready(c)
false
julia> put!(c, 1);
julia> isready(c)
true
Tamponlanmamış kanal:
julia> c = Channel();
julia> isready(c) # hiçbir görev put! için beklemiyor!
false
julia> task = Task(() -> put!(c, 1));
julia> schedule(task); # bir put! görevini planla
julia> isready(c)
true
Base.fetch
— Methodfetch(c::Channel)
İlk mevcut öğeyi Channel
'dan (kaldırmadan) bekler ve döndürür. Not: fetch
, bir tamponlanmamış (0-boyutlu) Channel
üzerinde desteklenmez.
Örnekler
Tamponlu kanal:
julia> c = Channel(3) do ch
foreach(i -> put!(ch, i), 1:3)
end;
julia> fetch(c)
1
julia> collect(c) # öğe kaldırılmaz
3-element Vector{Any}:
1
2
3
Base.close
— Methodclose(c::Channel[, excp::Exception])
Bir kanalı kapat. Bir istisna (isteğe bağlı olarak excp
ile verilen) aşağıdakilerden biri tarafından fırlatılır:
Base.bind
— Methodbind(chnl::Channel, task::Task)
chnl
'nin ömrünü bir görevle ilişkilendirir. Channel
chnl
, görev sona erdiğinde otomatik olarak kapatılır. Görevde yakalanmamış herhangi bir istisna, chnl
üzerindeki tüm bekleyenlere iletilir.
chnl
nesnesi, görev sona ermesinden bağımsız olarak açıkça kapatılabilir. Sona eren görevler, zaten kapatılmış Channel
nesneleri üzerinde hiçbir etkiye sahip değildir.
Bir kanal birden fazla göreve bağlandığında, ilk sona eren görev kanalı kapatır. Aynı göreve bağlı birden fazla kanal olduğunda, görevin sona ermesi tüm bağlı kanalları kapatır.
Örnekler
julia> c = Channel(0);
julia> task = @async foreach(i->put!(c, i), 1:4);
julia> bind(c,task);
julia> for i in c
@show i
end;
i = 1
i = 2
i = 3
i = 4
julia> isopen(c)
false
julia> c = Channel(0);
julia> task = @async (put!(c, 1); error("foo"));
julia> bind(c, task);
julia> take!(c)
1
julia> put!(c, 1);
HATA: TaskFailedException
Stacktrace:
[...]
nested task error: foo
[...]
Low-level synchronization using schedule
and wait
schedule
en kolay doğru kullanımı henüz başlamamış (planlanmış) bir Task
üzerindedir. Ancak, 4d61726b646f776e2e436f64652822222c20227363686564756c652229_40726566
ve wait
senkronizasyon arayüzleri oluşturmak için çok düşük seviyeli bir yapı taşı olarak kullanılabilir. schedule(task)
çağrısının kritik bir ön koşulu, çağrının task
'ı "sahiplenmesi" gerektiğidir; yani, schedule(task)
çağrısını yapan kodun, verilen task
içindeki wait
çağrısının nerelerde gerçekleştiğini bilmesi gerekir. Bu tür bir ön koşulu sağlamak için bir strateji, aşağıdaki örnekte gösterildiği gibi atomikler kullanmaktır:
@enum OWEState begin
OWE_EMPTY
OWE_WAITING
OWE_NOTIFYING
end
mutable struct OneWayEvent
@atomic state::OWEState
task::Task
OneWayEvent() = new(OWE_EMPTY)
end
function Base.notify(ev::OneWayEvent)
state = @atomic ev.state
while state !== OWE_NOTIFYING
# Spin until we successfully update the state to OWE_NOTIFYING:
state, ok = @atomicreplace(ev.state, state => OWE_NOTIFYING)
if ok
if state == OWE_WAITING
# OWE_WAITING -> OWE_NOTIFYING transition means that the waiter task is
# already waiting or about to call `wait`. The notifier task must wake up
# the waiter task.
schedule(ev.task)
else
@assert state == OWE_EMPTY
# Since we are assuming that there is only one notifier task (for
# simplicity), we know that the other possible case here is OWE_EMPTY.
# We do not need to do anything because we know that the waiter task has
# not called `wait(ev::OneWayEvent)` yet.
end
break
end
end
return
end
function Base.wait(ev::OneWayEvent)
ev.task = current_task()
state, ok = @atomicreplace(ev.state, OWE_EMPTY => OWE_WAITING)
if ok
# OWE_EMPTY -> OWE_WAITING transition means that the notifier task is guaranteed to
# invoke OWE_WAITING -> OWE_NOTIFYING transition. The waiter task must call
# `wait()` immediately. In particular, it MUST NOT invoke any function that may
# yield to the scheduler at this point in code.
wait()
else
@assert state == OWE_NOTIFYING
# Otherwise, the `state` must have already been moved to OWE_NOTIFYING by the
# notifier task.
end
return
end
ev = OneWayEvent()
@sync begin
@async begin
wait(ev)
println("done")
end
println("notifying...")
notify(ev)
end
# output
notifying...
done
OneWayEvent
, bir görevin başka bir görevin notify
'sini beklemesine
izin verir. Bu, wait
'in tek bir görevden yalnızca bir kez kullanılabileceği için sınırlı bir iletişim arayüzüdür (not: ev.task
'ın atomik olmayan ataması).
Bu örnekte, notify(ev::OneWayEvent)
yalnızca o OWE_WAITING
durumunu OWE_NOTIFYING
durumuna değiştiriyorsa schedule(ev.task)
çağrısına izin verilir. Bu, wait(ev::OneWayEvent)
işlemini gerçekleştiren görevin artık ok
dalında olduğunu ve @atomicreplace(ev.state, state => OWE_NOTIFYING)
işleminin başarısız olacağı için schedule(ev.task)
çağrısında bulunan başka görevlerin olamayacağını bilmemizi sağlar.