Hancurkan: multithreading, coroutine, async & tunggu

gambar

Kata Pengantar


Biarkan saya mengingatkan Anda bahwa bahasa ini dikembangkan oleh saya untuk tujuan pendidikan sebagai bagian dari hobi. Saya tidak menganggapnya (saat ini) bahasa yang dikembangkan dengan sempurna, tetapi siapa yang tahu apa yang bisa diharapkan di masa depan.

Jika Anda memiliki keinginan untuk mencobanya sendiri - unduh repositori proyek, di dalamnya Anda dapat menemukan versi rakitan proyek atau merakitnya sendiri, untuk OS Anda.

Pendahuluan


Multithreading dan asinkronisme di zaman kita adalah salah satu komponen terpenting dari bahasa pemrograman modern.

Oleh karena itu, saya memutuskan untuk menambahkan dukungan untuk desain dan teknologi modern ke bahasa pemrograman saya, sebagian dengan menambahkan desain sederhana dan nyaman untuk bahasa tersebut.

Aliran cepat


Mereka membuatnya mudah untuk memparalelasikan eksekusi kode.
Untuk ini, peluncuran: ... akhir konstruksi telah ditambahkan ke Mash

Contoh kode:

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

Contoh Keluaran:

 9 1 2 7 5 3 10 4 6 8 

Ketika eksekusi program mencapai launch..end, kode di dalam blok ini diluncurkan di utas terpisah, dan eksekusi kode peluncuran ditransfer ke blok ini.

Anda dapat bertemu dengan konstruksi bahasa yang hampir sama sebelumnya dalam bahasa pemrograman Kotlin.

Async & tunggu


Implementasi coroutine saja tidak cukup bagi saya, itu sebabnya konstruksi async & wait juga ditambahkan ke Mash.

Async memungkinkan Anda untuk menerjemahkan eksekusi kode ke utas terpisah dan melanjutkan eksekusi kode utama.

Tunggu memungkinkan Anda untuk menunggu saat ketika semua blok async yang diperlukan selesai.

Contoh kode:

 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 

Kesimpulan:

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

Multithreading klasik


Basis kode utama yang menyediakan dukungan untuk multithreading terkonsentrasi di modul? <threads>.

Komponen utama yang akan dibahas nanti:

1) Kelas TThread (hanya deklarasi kelas yang diberikan, kode lengkapnya terletak lebih jauh dalam 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) Kelas TCriticalSection (deskripsinya):

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


3) Metode untuk membuat dan memulai utas dengan cepat:
 func Async(method, ...) func Thread(method, ...) func Parallel(method, ...) 


4) Atom thread-safe (kelas variabel untuk interaksi cross-thread):
 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 


Jadi, mari kita bereskan.

Kelas TThread memungkinkan kita untuk membuat kelas penerus baru berdasarkannya, menambahkan variabel yang diperlukan ke bidangnya, yang akan ditransfer ke utas baru.

Kode sampel segera:

 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 

Jika kita terlalu malas untuk menggambarkan kelas baru untuk membuat aliran, maka kita dapat mengingat dukungan untuk redefinisi dinamis metode dalam instance kelas dan menggunakannya.

Contoh kode:

 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 

Jika kita hanya perlu menjalankan metode dengan parameter di utas baru, maka metode async (), thread () dan parallel () adalah persis apa yang kita butuhkan.

Contoh memulai metode di utas baru:

 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 

Seperti yang mungkin Anda perhatikan sebelumnya, 3 metode ini adalah fungsi dan mereka mengembalikan - kelas yang mirip dengan TThread.

Perbedaannya adalah bahwa async () membuat utas yang, setelah selesai, membebaskan memori dari dirinya sendiri, dan turunan dari kelas TThread secara otomatis dihapus,
utas () sama dengan async (), hanya utas yang awalnya dibuat beku.
Dan akhirnya parallel () - membuat utas berjalan, yang setelah selesai tidak akan melakukan penghancuran diri, mis. kita dapat menggunakan metode apa pun dari kelas TThread, misalnya WaitFor () dan tidak takut akan kesalahan runtime. Satu-satunya peringatan - Anda harus menelepon Gratis () secara manual.

Sinkronisasi utas


Untuk melakukan ini, saya menambahkan kelas TCriticalSection ke Mash.

Contoh kode:

 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 


Atom


Terapkan wadah yang aman untuk menyimpan nilai apa pun.

Contoh kode:
 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 


Coroutine


Fungsionalitas ini memungkinkan Anda untuk menyinkronkan eksekusi kode secara paralel.

Contoh kode:
 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 


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


Kesimpulan


Saya harap Anda menemukan artikel ini menarik.

Menunggu komentar :)

PS: Menurut komentar Anda, saya menghapus konstruk sampai..end dari bahasa. Sekarang tempatnya diambil oleh konstruksi:

 whilst <>: ... end 

Ini adalah loop sementara reguler, dengan perbedaan bahwa kondisi diperiksa setelah iterasi.

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


All Articles