Multi-Threading

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

ماكرو لتنفيذ حلقة for بالتوازي. يتم توزيع مساحة التكرار على مهام ذات حبيبات خشنة. يمكن تحديد هذه السياسة بواسطة وسيط schedule. ينتظر تنفيذ الحلقة تقييم جميع التكرارات.

انظر أيضًا: @spawn و pmap في Distributed.

مساعدة موسعة

الدلالات

ما لم يتم تحديد ضمانات أقوى بواسطة خيار الجدولة، فإن الحلقة التي يتم تنفيذها بواسطة ماكرو @threads لها الدلالات التالية.

تنفذ ماكرو @threads جسم الحلقة بترتيب غير محدد وبتزامن محتمل. لا تحدد التعيينات الدقيقة للمهام وخيوط العمل. يمكن أن تكون التعيينات مختلفة في كل تنفيذ. يجب ألا تفترض شفرة جسم الحلقة (بما في ذلك أي شفرة يتم استدعاؤها بشكل غير مباشر منها) أي افتراضات حول توزيع التكرارات على المهام أو خيط العمل الذي يتم تنفيذها فيه. يجب أن يكون جسم الحلقة لكل تكرار قادرًا على إحراز تقدم مستقل عن التكرارات الأخرى وأن يكون خاليًا من سباقات البيانات. على هذا النحو، قد تؤدي التزامنات غير الصالحة عبر التكرارات إلى توقف بينما قد تؤدي الوصولات غير المتزامنة إلى سلوك غير محدد.

على سبيل المثال، تشير الشروط أعلاه إلى أن:

  • يجب أن يتم تحرير القفل المأخوذ في تكرار داخل نفس التكرار.
  • التواصل بين التكرارات باستخدام بدائل حظر مثل Channels غير صحيح.
  • الكتابة فقط إلى المواقع غير المشتركة عبر التكرارات (ما لم يتم استخدام قفل أو عملية ذرية).
  • ما لم يتم استخدام الجدول الزمني :static، قد تتغير قيمة threadid() حتى داخل تكرار واحد. انظر Task Migration.

المجدولون

بدون وسيط المجدول، فإن الجدولة الدقيقة غير محددة وتختلف عبر إصدارات جوليا. حاليًا، يتم استخدام :dynamic عندما لا يتم تحديد المجدول.

جوليا 1.5

وسيط schedule متاح اعتبارًا من جوليا 1.5.

:dynamic (افتراضي)

ينفذ المجدول :dynamic التكرارات ديناميكيًا إلى خيوط العمل المتاحة. تفترض التنفيذ الحالي أن عبء العمل لكل تكرار متساوي. ومع ذلك، قد تتم إزالة هذا الافتراض في المستقبل.

تعتبر هذه الخيار الجدولي مجرد تلميح لآلية التنفيذ الأساسية. ومع ذلك، يمكن توقع بعض الخصائص. عدد Tasks المستخدمة بواسطة المجدول :dynamic محدود بعدد صغير مضاعف لعدد خيوط العمل المتاحة (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 ميكروثانية).

جوليا 1.8

خيار :dynamic لوسيط schedule متاح وهو الافتراضي اعتبارًا من جوليا 1.8.

:greedy

يولد المجدول :greedy ما يصل إلى Threads.threadpoolsize() مهام، كل منها تعمل بشغف على القيم المتكررة المعطاة عند إنتاجها. بمجرد أن تنتهي مهمة واحدة من عملها، تأخذ القيمة التالية من المكرر. العمل الذي تقوم به أي مهمة فردية ليس بالضرورة على قيم متجاورة من المكرر. قد ينتج المكرر المعطى قيمًا إلى الأبد، فقط واجهة المكرر مطلوبة (لا يوجد فهرسة).

يعتبر هذا الخيار الجدولي عمومًا خيارًا جيدًا إذا كان عبء العمل للتكرارات الفردية غير متساوي/لديه انتشار كبير.

جوليا 1.11

خيار :greedy لوسيط schedule متاح اعتبارًا من جوليا 1.11.

:static

ينشئ المجدول :static مهمة واحدة لكل خيط ويقسم التكرارات بالتساوي بينها، مع تعيين كل مهمة بشكل محدد لكل خيط. على وجه الخصوص، فإن قيمة threadid() مضمونة أن تكون ثابتة داخل تكرار واحد. يعتبر تحديد :static خطأ إذا تم استخدامه من داخل حلقة @threads أخرى أو من خيط غير 1.

Note

يوجد جدولة :static لدعم انتقال الشفرة المكتوبة قبل جوليا 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 ثانية لإكمال حلقة for.

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

مماثل لـ foreach(f, channel)، ولكن يتم تقسيم التكرار عبر channel واستدعاءات f عبر ntasks مهام تم إنشاؤها بواسطة Threads.@spawn. ستنتظر هذه الدالة حتى تكتمل جميع المهام التي تم إنشاؤها داخليًا قبل أن تعود.

إذا كان schedule isa FairSchedule، ستحاول Threads.foreach إنشاء مهام بطريقة تمكن جدولة جوليا من توزيع عناصر العمل عبر الخيوط بشكل أكثر حرية. هذه الطريقة عمومًا لها تكلفة أعلى لكل عنصر، ولكن قد تؤدي بشكل أفضل من 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]
جوليا 1.6

تتطلب هذه الدالة جوليا 1.6 أو أحدث.

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

أنشئ Task وschedule لتشغيله على أي خيط متاح في مجموعة الخيوط المحددة (:default إذا لم يتم تحديدها). يتم تخصيص المهمة لخيط بمجرد أن يصبح متاحًا. للانتظار حتى تنتهي المهمة، قم باستدعاء wait على نتيجة هذه الماكرو، أو قم باستدعاء fetch للانتظار ثم الحصول على قيمة الإرجاع الخاصة بها.

يمكن إدخال القيم في @spawn عبر $، والذي ينسخ القيمة مباشرة إلى الإغلاق الأساسي المُنشأ. وهذا يسمح لك بإدراج قيمة متغير، مما يعزل الكود غير المتزامن عن التغييرات في قيمة المتغير في المهمة الحالية.

Note

قد يتغير الخيط الذي تعمل عليه المهمة إذا قامت المهمة بالتخلي، لذلك يجب عدم اعتبار threadid() ثابتًا لمهمة. انظر Task Migration، والدليل الأوسع حول التعددية في الخيوط لمزيد من التحذيرات المهمة. انظر أيضًا الفصل حول مجموعات الخيوط.

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 1.

أمثلة

julia> Threads.threadid()
1

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

!!! ملاحظة قد يتغير الخيط الذي تعمل عليه مهمة ما إذا كانت المهمة تتخلى، وهو ما يعرف بـ هجرة المهام. لهذا السبب، في معظم الحالات، ليس من الآمن استخدام threadid() للقيام بفهرسة، على سبيل المثال، مصفوفة من الكائنات المؤقتة أو ذات الحالة.

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

احصل على حد أدنى لعدد الخيوط (عبر جميع مجموعات الخيوط) المتاحة لعملية جوليا، مع دلالات الاستحواذ الذري. ستكون النتيجة دائمًا أكبر من أو تساوي threadid() بالإضافة إلى threadid(task) لأي مهمة كنت قادرًا على ملاحظتها قبل استدعاء maxthreadid.

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

احصل على العدد الحالي من الخيوط داخل مجموعة الخيوط المحددة. الخيوط في :interactive لها أرقام تعريف 1:nthreads(:interactive)، والخيوط في :default لها أرقام تعريف في nthreads(:interactive) .+ (1:nthreads(:default)).

انظر أيضًا BLAS.get_num_threads و BLAS.set_num_threads في مكتبة LinearAlgebra القياسية، و nprocs() في مكتبة Distributed القياسية و Threads.maxthreadid().

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

يعيد مجموعة خيوط الخيط المحدد؛ إما :default أو :interactive أو :foreign.

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

احصل على عدد الخيوط المتاحة لمجموعة الخيوط الافتراضية (أو لمجموعة الخيوط المحددة).

انظر أيضًا: BLAS.get_num_threads و BLAS.set_num_threads في مكتبة LinearAlgebra القياسية، و nprocs() في مكتبة Distributed القياسية.

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

يُرجع عدد خيوط جمع القمامة (GC) المكونة حاليًا. يشمل ذلك كل من خيوط العلامة وخيوط المسح المتزامن.

source

انظر أيضًا Multi-Threading.

Atomic operations

atomicKeyword

عمليات المؤشر غير الآمنة متوافقة مع تحميل وتخزين المؤشرات المعلنة باستخدام _Atomic و std::atomic في C11 و C++23 على التوالي. قد يتم إلقاء خطأ إذا لم يكن هناك دعم لتحميل نوع جوليا T بشكل ذري.

انظر أيضًا: unsafe_load، unsafe_modify!، unsafe_replace!، unsafe_store!، unsafe_swap!

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

قم بتحديد var أو ex على أنه يتم تنفيذه بشكل ذري، إذا كانت ex تعبيرًا مدعومًا. إذا لم يتم تحديد order، فإنه يت default إلى :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 # 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 => 10
Julia 1.7

هذه الوظيفة تتطلب على الأقل Julia 1.7.

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

يخزن new في a.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 # استبدال الحقل x من a بـ 4، مع اتساق تسلسلي
1

julia> @atomic a.x # جلب الحقل x من a، مع اتساق تسلسلي
4
جوليا 1.7

تتطلب هذه الوظيفة على الأقل جوليا 1.7.

source
Base.@atomicreplaceMacro
@atomicreplace a.b.x متوقع => مرغوب
@atomicreplace :sequentially_consistent a.b.x متوقع => مرغوب
@atomicreplace :sequentially_consistent :monotonic a.b.x متوقع => مرغوب

قم بإجراء الاستبدال الشرطي المعبر عنه بواسطة الزوج بشكل ذري، مع إرجاع القيم (قديم، نجاح::Bool). حيث تشير نجاح إلى ما إذا كان الاستبدال قد اكتمل.

تترجم هذه العملية إلى استدعاء replaceproperty!(a.b, :x, متوقع, مرغوب).

انظر إلى الذرات حسب الحقل في القسم الخاص باليد في الدليل لمزيد من التفاصيل.

أمثلة

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

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

julia> @atomicreplace a.x 1 => 2 # استبدل الحقل x من a بـ 2 إذا كان 1، مع اتساق تسلسلي
(قديم = 1، نجاح = true)

julia> @atomic a.x # احصل على الحقل x من a، مع اتساق تسلسلي
2

julia> @atomicreplace a.x 1 => 2 # استبدل الحقل x من a بـ 2 إذا كان 1، مع اتساق تسلسلي
(قديم = 2، نجاح = false)

julia> xchg = 2 => 0; # استبدل الحقل x من a بـ 0 إذا كان 2، مع اتساق تسلسلي

julia> @atomicreplace a.x xchg
(قديم = 2، نجاح = true)

julia> @atomic a.x # احصل على الحقل x من a، مع اتساق تسلسلي
0
جوليا 1.7

تتطلب هذه الوظيفة على الأقل جوليا 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 # تعيين الحقل x من a إلى 1، إذا كان غير محدد، مع اتساق تسلسلي
true

julia> @atomic a.x # جلب الحقل x من a، مع اتساق تسلسلي
1

julia> @atomiconce a.x = 1 # تعيين الحقل x من a إلى 1، إذا كان غير محدد، مع اتساق تسلسلي
false
Julia 1.11

تتطلب هذه الوظيفة على الأقل Julia 1.11.

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

حجم ثابت DenseVector{T}. الوصول إلى أي من عناصره يتم بشكل ذري (مع ترتيب :monotonic). يجب أن يتم تعيين أي من العناصر باستخدام ماكرو @atomic وتحديد الترتيب بشكل صريح.

Warning

كل عنصر هو ذري بشكل مستقل عند الوصول إليه، ولا يمكن تعيينه بشكل غير ذري. حاليًا، لم يتم إكمال ماكرو @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

تعتبر واجهات برمجة التطبيقات التالية قديمة، على الرغم من أن الدعم لها من المحتمل أن يستمر لعدة إصدارات.

Base.Threads.AtomicType
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!، إلخ.

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

قارن وعيّن x بشكل ذري

يقارن بشكل ذري القيمة في x مع cmp. إذا كانت متساوية، يكتب newval إلى x. خلاف ذلك، يترك x دون تعديل. يُرجع القيمة القديمة في x. من خلال مقارنة القيمة المعادة مع cmp (عبر ===) يعرف المرء ما إذا كان x قد تم تعديله والآن يحمل القيمة الجديدة newval.

للحصول على مزيد من التفاصيل، انظر إلى تعليمات cmpxchg في LLVM.

يمكن استخدام هذه الدالة لتنفيذ دلالات المعاملات. قبل المعاملة، يتم تسجيل القيمة في 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 بشكل ذري. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، راجع تعليمات atomicrmw xchg في LLVM.

أمثلة

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

يضيف val إلى x بشكل ذري

ينفذ x[] += val بشكل ذري. يُرجع القيمة القديمة. غير معرف لـ Atomic{Bool}.

للحصول على مزيد من التفاصيل، انظر إلى تعليمات atomicrmw add في LLVM.

أمثلة

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

يطرح val من x بشكل ذري

ينفذ x[] -= val بشكل ذري. يُرجع القيمة القديمة. غير معرف لـ Atomic{Bool}.

للحصول على مزيد من التفاصيل، انظر إلى تعليمات atomicrmw sub في LLVM.

أمثلة

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

يؤدي عملية "AND" بتنسيق ذري بين x و val

يؤدي x[] &= val بشكل ذري. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، راجع تعليمات atomicrmw and في LLVM.

أمثلة

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 (ليس-و) بتنسيق ذري بين x و val

يؤدي إلى x[] = ~(x[] & val) بشكل ذري. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، راجع تعليمات atomicrmw nand في LLVM.

أمثلة

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

يؤدي عملية OR بتنسيق ذري بين x و val

يؤدي x[] |= val بشكل ذري. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، راجع تعليمات atomicrmw or في LLVM.

أمثلة

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

يؤدي إلى عملية XOR (الاستبعاد) بتزامن بين x و val

يؤدي إلى x[] $= val بشكل متزامن. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، انظر إلى تعليمات atomicrmw xor في LLVM.

أمثلة

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

قم بتخزين الحد الأقصى لـ x و val في x بشكل ذري

يؤدي إلى x[] = max(x[], val) بشكل ذري. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، راجع تعليمات atomicrmw max في LLVM.

أمثلة

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

قم بتخزين الحد الأدنى من x و val في x بشكل ذري

يؤدي إلى x[] = min(x[], val) بشكل ذري. يُرجع القيمة القديمة.

للحصول على مزيد من التفاصيل، راجع تعليمات atomicrmw min في LLVM.

أمثلة

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()

أدخل سياج ذاكرة بتناسق تسلسلي

يُدخل سياج ذاكرة مع دلالات ترتيب بتناسق تسلسلي. هناك خوارزميات حيث يكون هذا مطلوبًا، أي حيث يكون ترتيب الاستحواذ/الإفراج غير كافٍ.

من المحتمل أن تكون هذه عملية مكلفة جدًا. نظرًا لأن جميع العمليات الذرية الأخرى في جوليا تحتوي بالفعل على دلالات الاستحواذ/الإفراج، يجب ألا تكون السياجات الصريحة ضرورية في معظم الحالات.

للحصول على مزيد من التفاصيل، راجع تعليمات fence في LLVM.

source

ccall using a libuv threadpool (Experimental)

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

يتم استدعاء ماكرو @threadcall بنفس طريقة استدعاء ccall ولكن يقوم بالعمل في خيط مختلف. هذا مفيد عندما تريد استدعاء دالة C محجوزة دون التسبب في حظر الخيط الحالي لـ julia. يتم تحديد التزامن بواسطة حجم مجموعة خيوط libuv، والتي تكون افتراضيًا 4 خيوط ولكن يمكن زيادتها عن طريق تعيين متغير البيئة UV_THREADPOOL_SIZE وإعادة تشغيل عملية julia.

لاحظ أن الدالة المستدعاة يجب ألا تستدعي مرة أخرى داخل Julia.

source

Low-level synchronization primitives

تُستخدم هذه الكتل الأساسية لإنشاء كائنات التزامن العادية.

Base.Threads.SpinLockType
SpinLock()

أنشئ قفل دوران غير قابل لإعادة الدخول، يستخدم اختبارًا واختبارًا وتعيينًا. سيؤدي الاستخدام التكراري إلى حدوث حالة توقف. يجب استخدام هذا النوع من الأقفال فقط حول الشيفرة التي تستغرق وقتًا قليلاً للتنفيذ ولا تعيق (مثل إجراء الإدخال/الإخراج). بشكل عام، يجب استخدام ReentrantLock بدلاً من ذلك.

يجب أن يتطابق كل lock مع unlock. إذا كانت !islocked(lck::SpinLock) صحيحة، فإن trylock(lck) ينجح ما لم تكن هناك مهام أخرى تحاول الاحتفاظ بالقفل "في نفس الوقت".

تكون أقفال الدوران التي تستخدم اختبارًا واختبارًا وتعيينًا الأسرع حتى حوالي 30 خيطًا متنافسًا. إذا كان لديك المزيد من التنافس من ذلك، يجب النظر في أساليب تزامن مختلفة.

source