الهريس: multithreading ، coroutines ، المزامنة والانتظار

الصورة

مقدمة


اسمحوا لي أن أذكركم أن هذه اللغة قد طورتها لي لأغراض تعليمية كجزء من هواية. لا أعتبرها (في الوقت الحالي) لغة متطورة بشكل مثالي ، لكن من يعرف ما يمكن أن يتوقعه المستقبل.

إذا كنت ترغب في تجربة ذلك بنفسك - قم بتنزيل مستودع المشروع ، فيمكنك العثور على الإصدار المجمع من المشروع أو تجميعه بنفسك ، لنظام التشغيل لديك.

مقدمة


تعد تعدد مؤشرات الترابط وعدم التزامن في عصرنا أحد أهم مكونات لغات البرمجة الحديثة.

لذلك ، قررت إضافة دعم للتصميمات والتقنيات الحديثة إلى لغتي البرمجية ، وذلك جزئيًا بإضافة تصميمات بسيطة وملائمة للغة.

التدفقات السريعة


أنها تجعل من السهل موازاة تنفيذ التعليمات البرمجية.
لهذا ، تم إطلاق: ... نهاية البناء إلى Mash

مثال على الكود:

uses <bf> uses <crt> proc main(): for(i ?= 1; i <= 10; i++): launch: sleep(random() * 100) println(i) end end inputln() end 

مثال الإخراج:

 9 1 2 7 5 3 10 4 6 8 

عندما يصل تنفيذ البرنامج إلى launch..end ، يتم إطلاق الكود الموجود داخل هذه الكتلة في خيط منفصل ، ويتم نقل تنفيذ كود الإطلاق إلى هذه الكتلة.

يمكنك تلبية نفس اللغة تقريبًا في وقت سابق من لغة برمجة Kotlin.

متزامن وانتظر


لا يكفي تطبيق coroutines بمفرده ، ولهذا السبب يتم أيضًا إضافة تصميمات التزامن والانتظار إلى Mash.

يتيح لك Async ترجمة تنفيذ التعليمات البرمجية إلى مؤشر ترابط منفصل ومتابعة تنفيذ التعليمات البرمجية الرئيسية.

الانتظار يسمح لك بالانتظار حتى اللحظة التي يتم فيها استكمال جميع كتل المزامنة الضرورية.

مثال على الكود:

 uses <bf> uses <crt> proc main(): println("Hello!") async a: println("Test") sleep(1000) println("Test") sleep(1000) println("Test") sleep(1000) end async b: println("Test 2") sleep(300) println("Test 2") sleep(300) println("Test 2") sleep(300) end wait a, b println("End!") inputln() end 

الخلاصة:

 Hello! Test Test 2 Test 2 Test 2 Test Test End! 

multithreading الكلاسيكية


تتركز قاعدة الشفرة الرئيسية التي توفر الدعم للتعددية في الوحدة النمطية؟ <threads>.

المكونات الرئيسية التي سيتم مناقشتها لاحقًا:

1) فئة TThread (يتم إعطاء إعلان فئة فقط ، الرمز الكامل موجود في الوحدة النمطية):

 class TThread: protected: var ThreadContext public: var Resumed, Terminated, FreeOnTerminate proc Create, Free proc Execute //for overriding proc Suspend, Resume, Terminate, WaitFor, ReJoin //Control proc's end 

2) فئة TCriticalSection (وصفها):

 class TCriticalSection: protected: var Critical_Section_Controller public: proc Create, Free //Methods proc Enter, Leave func TryEnter end 


3) طرق لإنشاء وبدء المواضيع بسرعة:
 func Async(method, ...) func Thread(method, ...) func Parallel(method, ...) 


4) الذري الآمن الموضوع (فئة متغير للتفاعل عبر الصفحات):
 class TAtomic: private: var Locker, Value public: proc Create, Free proc Set func Get end 


5) Coroutines:
 class TCoroutine(TThread): public: var NextCoroutine proc Create proc Yield, YieldFor end 


لذلك ، دعونا نأخذه بالترتيب.

تسمح لنا فئة TThread بإنشاء فئة لاحقة جديدة بناءً عليها ، مع إضافة المتغيرات الضرورية إلى حقولها ، والتي سيتم نقلها إلى سلسلة الرسائل الجديدة.

نموذج التعليمات البرمجية على الفور:

 uses <bf> uses <crt> uses <threads> class MyThreadClass(TThread): var Param proc Create, Execute end proc MyThreadClass::Create(Param): $Param ?= Param TThread::Create$(true) end proc MyThreadClass::Execute(): for(i ?= 0; i < 10; i++): PrintLn(i, ": ", $Param) end end proc main(): new MyThreadClass("Thread #2!") InputLn() end 

إذا كنا كسولًا للغاية لوصف فئة جديدة لإنشاء دفق ، فيمكننا أن نتذكر دعم إعادة التعريف الديناميكي للطرق في مثيلات الفصل واستخدامها.

مثال على الكود:

 uses <bf> uses <crt> uses <threads> proc class::MyThreadedProc(): for(i ?= 0; i < 10; i++): PrintLn(i, ": Threaded hello!") end end proc main(): Thr ?= new TThread(false) Thr->Execute ?= class::MyThreadedProc Thr->Resume() InputLn() end 

إذا كنا نحتاج فقط إلى تشغيل الطريقة مع المعلمات في سلسلة رسائل جديدة ، فإن طرق async () و thread () و parallel () هي بالضبط ما نحتاجه.

مثال لبدء عملية في سلسلة رسائل جديدة:

 uses <bf> uses <crt> uses <threads> proc ThreadedProc(Arg): for(i ?= 0; i < 10; i++): PrintLn(i, ": ", Arg) end end proc main(): Async(ThreadedProc, "Thread #1!") InputLn() end 

كما لاحظت سابقًا ، هذه الطرق الثلاثة هي وظائف ويعودون - فئات مشابهة لـ TThread.

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

تزامن الموضوع


للقيام بذلك ، أضفت فئة TCriticalSection إلى Mash.

مثال على الكود:

 uses <bf> uses <crt> uses <threads> var CSect = new TCriticalSection() proc ThreadedProc(Arg): while true: CSect -> Enter() PrintLn(Arg) CSect -> Leave() Sleep(10) gc() end end proc CriticalThreadedProc(): while true: Sleep(3000) CSect -> Enter() Sleep(1000) PrintLn("And now...") Sleep(1000) PrintLn("Time to...") Sleep(1000) PrintLn("Critical section!") Sleep(3000) CSect -> Leave() gc() end end proc main(): Async(ThreadedProc, "I'm thread #1!!!") Async(CriticalThreadedProc) InputLn() end 


ذري


تطبيق حاوية مؤشر ترابط آمن لتخزين أي قيم.

مثال على الكود:
 uses <bf> uses <crt> uses <threads> proc main(): MyThreadValue ?= new TAtomic(0) launch: while true: MyThreadValue -> Set(1) Sleep(8) gc() end end launch: while true: MyThreadValue -> Set(2) Sleep(3) gc() end end launch: while true: MyThreadValue -> Set(3) Sleep(11) gc() end end while true: PrintLn(MyThreadValue -> Get()) Sleep(100) gc() end end 


Coroutines


تتيح لك هذه الوظيفة مزامنة التنفيذ المتوازي للرمز.

مثال على الكود:
 uses <bf> uses <crt> uses <threads> proc class::Proc1(): while true: println("Hello world #1") sleep(100) gc() $yield() end end proc class::Proc2(): while true: println("Hello world #2") sleep(100) gc() $yield() end end proc class::Proc3(): while true: println("Hello world #3") sleep(100) gc() $yield() end end proc main(): cor3 ?= new TCoroutine(false, null) cor3 -> Execute ?= class::Proc3 cor2 ?= new TCoroutine(false, cor3) cor2 -> Execute ?= class::Proc2 cor1 ?= new TCoroutine(false, cor2) cor1 -> Execute ?= class::Proc1 cor3 -> NextCoroutine ?= cor1 cor1 -> Resume() InputLn() end 


الخلاصة:
 Hello world #1 Hello world #2 Hello world #3 Hello world #1 Hello world #2 Hello world #3 ... 


الخاتمة


آمل أن تجد هذا المقال مثيرًا للاهتمام.

في انتظار التعليقات :)

ملاحظة: وفقًا لتعليقاتك ، قمت بإزالة الإنشاء حتى .. من اللغة. الآن يتم أخذ مكانها عن طريق البناء:

 whilst <>: ... end 

إنها حلقة منتظمة ، مع اختلاف فحص الحالة بعد التكرار.

Source: https://habr.com/ru/post/ar442560/


All Articles