Mash: Multithreading, Coroutinen, Async & Wait

Bild

Vorwort


Ich möchte Sie daran erinnern, dass diese Sprache von mir zu Bildungszwecken im Rahmen eines Hobbys entwickelt wurde. Ich halte es (im Moment) nicht für eine ideal entwickelte Sprache, aber wer weiß, was die Zukunft erwarten kann.

Wenn Sie es selbst in Aktion ausprobieren möchten, laden Sie das Projekt- Repository herunter. Dort finden Sie die zusammengestellte Version des Projekts oder können sie selbst für Ihr Betriebssystem zusammenstellen.

Einführung


Multithreading und Asynchronismus sind in unserer Zeit eine der wichtigsten Komponenten moderner Programmiersprachen.

Aus diesem Grund habe ich beschlossen, meiner Programmiersprache Unterstützung für moderne Designs und Technologien hinzuzufügen, indem ich der Sprache teilweise einfache und bequeme Designs hinzufügte.

Schnelle Flüsse


Sie erleichtern die Parallelisierung der Codeausführung.
Zu diesem Zweck wurde der Start: ... Endkonstruktion zu Mash hinzugefügt

Codebeispiel:

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

Ausgabebeispiel:

 9 1 2 7 5 3 10 4 6 8 

Wenn die Ausführung des Programms den Start erreicht hat, wird der Code in diesem Block in einem separaten Thread gestartet und die Ausführung des Startcodes in diesen Block übertragen.

Sie könnten fast das gleiche Sprachkonstrukt früher in der Programmiersprache Kotlin treffen.

Async & warte


Die Implementierung von Coroutinen allein reicht mir nicht aus, deshalb werden Mash auch Async & Wait-Konstrukte hinzugefügt.

Mit Async können Sie die Codeausführung in einen separaten Thread übersetzen und die Ausführung des Hauptcodes fortsetzen.

Mit Warten können Sie auf den Moment warten, in dem alle erforderlichen asynchronen Blöcke abgeschlossen sind.

Codebeispiel:

 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 

Fazit:

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

Klassisches Multithreading


Die Hauptcodebasis, die Multithreading unterstützt, konzentriert sich auf das Modul? <Threads>.

Die Hauptkomponenten, die später besprochen werden:

1) TThread-Klasse (es wird nur eine Klassendeklaration angegeben, der vollständige Code befindet sich weiter im Modul):

 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-Klasse (ihre Beschreibung):

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


3) Methoden zum schnellen Erstellen und Starten von Threads:
 func Async(method, ...) func Thread(method, ...) func Parallel(method, ...) 


4) Thread-sicheres Atom (variable Klasse für Cross-Thread-Interaktion):
 class TAtomic: private: var Locker, Value public: proc Create, Free proc Set func Get end 


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


Nehmen wir es also in Ordnung.

Mit der TThread-Klasse können wir darauf basierend eine neue Nachfolgerklasse erstellen und die erforderlichen Variablen zu ihren Feldern hinzufügen, die in den neuen Thread übertragen werden.

Beispielcode sofort:

 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 

Wenn wir zu faul sind, eine neue Klasse zu beschreiben, um einen Stream zu erstellen, können wir die Unterstützung für die dynamische Neudefinition von Methoden in Klasseninstanzen zurückrufen und verwenden.

Codebeispiel:

 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 

Wenn wir die Methode nur mit den Parametern in einem neuen Thread ausführen müssen, sind die Methoden async (), thread () und parallel () genau das, was wir brauchen.

Ein Beispiel für das Starten einer Methode in einem neuen Thread:

 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 

Wie Sie vielleicht schon früher bemerkt haben, sind diese drei Methoden Funktionen und geben Klassen zurück, die TThread ähnlich sind.

Ihr Unterschied besteht darin, dass async () einen Thread erstellt, der nach Abschluss den Speicher von sich selbst freigibt und eine Instanz der TThread-Klasse automatisch gelöscht wird.
thread () ist dasselbe wie async (), nur der Thread wird anfänglich eingefroren erstellt.
Und schließlich parallel () - erstellt einen laufenden Thread, der nach Abschluss keine Selbstzerstörung durchführt, d. H. Wir können alle Methoden der TThread-Klasse verwenden, zum Beispiel WaitFor (), und haben keine Angst vor Laufzeitfehlern. Die einzige Einschränkung - Sie müssen Free () manuell aufrufen.

Thread-Synchronisation


Zu diesem Zweck habe ich Mash die TCriticalSection-Klasse hinzugefügt.

Codebeispiel:

 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 


Atomic


Implementieren Sie einen thread-sicheren Container zum Speichern von Werten.

Codebeispiel:
 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 


Coroutinen


Mit dieser Funktion können Sie die parallele Ausführung von Code synchronisieren.

Codebeispiel:
 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 


Fazit:
 Hello world #1 Hello world #2 Hello world #3 Hello world #1 Hello world #2 Hello world #3 ... 


Fazit


Ich hoffe, Sie finden diesen Artikel interessant.

Warten auf Kommentare :)

PS: Ihren Kommentaren zufolge habe ich das Konstrukt till..end aus der Sprache entfernt. Jetzt nimmt der Bau seinen Platz ein:

 whilst <>: ... end 

Es ist eine reguläre while-Schleife, mit dem Unterschied, dass die Bedingung nach der Iteration überprüft wird.

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


All Articles