Alan Cox pernah berkata "Komputer adalah mesin negara. Utas adalah untuk orang yang tidak dapat memprogram mesin negara".
Karena bertanya kepada Alan secara langsung bukan merupakan pilihan bagi saya yang rendah hati, saya lebih suka bertanya di sini: bagaimana seseorang mencapai fungsionalitas multi-threading dalam bahasa tingkat tinggi, seperti Java, hanya menggunakan satu utas dan mesin negara? Misalnya, bagaimana jika ada 2 kegiatan untuk dilakukan (melakukan perhitungan dan melakukan I / O) dan satu kegiatan dapat diblokir?
Apakah menggunakan "hanya mesin negara" sebagai alternatif yang layak untuk multi-threading dalam bahasa tingkat tinggi?
multithreading
finite-state-machine
Victor Sorokin
sumber
sumber
Jawaban:
Semua yang dilakukan thread adalah operasi interleave sehingga bagian dari proses tersebut tampaknya tumpang tindih dalam waktu. Mesin single-core dengan beberapa utas hanya melompat-lompat: ia mengeksekusi potongan-potongan kecil kode dari satu utas, kemudian beralih ke utas lainnya. Penjadwal sederhana memutuskan utas mana yang merupakan prioritas tertinggi dan sebenarnya dieksekusi di inti.
Pada komputer single-core, tidak ada yang benar-benar terjadi "pada saat yang sama". Ini semua hanya eksekusi yang disisipkan.
Ada banyak, banyak cara untuk mencapai interleaving. Banyak.
Katakanlah Anda memiliki proses dua utas sederhana yang menggunakan kunci sederhana sehingga kedua utas dapat menulis ke variabel umum. Anda memiliki enam blok kode.
[Ini bisa dalam satu lingkaran atau memiliki lebih banyak kunci atau apa pun. Yang dilakukannya adalah lebih lama, tidak lebih kompleks.]
Langkah-langkah T1 harus dijalankan secara berurutan (T1-sebelum, T1-dengan, T1-setelah) dan langkah-langkah T2 harus dijalankan secara berurutan (T2-sebelum, T2-dengan, T2-setelah).
Selain kendala "dalam urutan", ini dapat disisipkan dengan cara apa pun. Bagaimanapun caranya. Mereka dapat dijalankan seperti yang tercantum di atas. Pemesanan lain yang valid adalah (T1-before, T2-before, T2-lock, T1-lock, T2-after, T1-after). Ada banyak pemesanan yang valid.
Tunggu.
Ini hanya mesin negara dengan enam negara.
Ini adalah automata keadaan terbatas non-deterministik. Urutan status T1-xxx dengan status T2-xxx tidak pasti, dan tidak masalah. Jadi ada tempat di mana "negara bagian selanjutnya" adalah lemparan koin.
Misalnya, ketika FSM dimulai, T1-before atau T2-before keduanya adalah status pertama yang sah. Lempar koin.
Katakanlah itu muncul T1-sebelumnya. Lakukan itu. Ketika itu selesai, ada pilihan antara T1-dengan dan T2-sebelumnya. Lempar koin.
Pada setiap langkah dalam FSM akan ada dua pilihan (dua utas - dua pilihan) dan lemparan koin dapat menentukan negara bagian mana yang diikuti.
sumber
Menulis fungsi pemblokiran adalah untuk orang yang tidak dapat membuat mesin negara;)
Utas berguna jika Anda tidak bisa menghindari pemblokiran. Tidak ada aktivitas komputer fundamental yang benar-benar menghalangi, hanya saja banyak dari mereka yang diimplementasikan dengan cara itu untuk kemudahan penggunaan. Alih-alih mengembalikan karakter atau "baca gagal", fungsi baca akan memblokir hingga seluruh buffer dibaca. Alih-alih memeriksa pesan balik dalam antrian, dan kembali jika tidak ada yang ditemukan, fungsi koneksi menunggu balasan.
Anda tidak dapat menggunakan fungsi pemblokiran di mesin negara (setidaknya satu yang tidak bisa "dibekukan").
Dan ya, menggunakan mesin negara adalah alternatif yang layak. Dalam sistem Real Time, ini adalah satu-satunya pilihan, sistem yang menyediakan kerangka kerja untuk mesin. Menggunakan utas dan fungsi pemblokiran hanyalah "jalan keluar yang mudah", karena biasanya satu panggilan ke fungsi pemblokiran menggantikan sekitar 3-4 status dalam mesin status.
sumber
Apa yang Anda gambarkan disebut kerja sama multitasking , di mana tugas diberikan CPU dan diharapkan untuk melepaskannya secara sukarela setelah beberapa waktu atau aktivitas yang ditentukan sendiri. Sebuah tugas yang tidak bekerja sama dengan terus menggunakan CPU atau dengan memblokir gusi seluruh pekerjaan dan singkat memiliki pengawas waktu perangkat keras, tidak ada kode yang mengawasi tugas yang dapat dilakukan tentang hal itu.
Apa yang Anda lihat dalam sistem modern disebut preemptive multitasking , yang mana tugas tidak harus melepaskan CPU karena supervisor melakukannya untuk mereka ketika interupsi yang dihasilkan perangkat keras tiba. Rutin layanan interupsi dalam penyelia menyimpan status CPU dan mengembalikannya di lain waktu tugas tersebut dianggap layak untuk sebuah irisan waktu, kemudian memulihkan keadaan dari tugas apa pun yang akan dijalankan berikutnya dan melompat kembali ke dalamnya seolah-olah tidak ada yang terjadi . Tindakan ini disebut saklar konteks dan bisa mahal.
Giat? Yakin. Sane? Terkadang. Apakah Anda menggunakan utas atau beberapa bentuk kerja sama multitasking yang diseduh sendiri di rumah (misalnya, mesin negara) tergantung pada pengorbanan yang ingin Anda lakukan.
Utas menyederhanakan desain tugas ke titik di mana Anda dapat memperlakukan masing-masing sebagai programnya sendiri yang kebetulan berbagi ruang data dengan yang lain. Ini memberi Anda kebebasan untuk fokus pada pekerjaan yang dihadapi dan tidak semua manajemen dan rumah tangga yang diperlukan untuk membuatnya iterasi pada suatu waktu. Tetapi karena tidak ada perbuatan baik yang tidak dihukum, Anda membayar untuk semua kemudahan ini dalam konteks. Memiliki banyak utas yang menghasilkan CPU setelah melakukan pekerjaan minimal (secara sukarela atau dengan melakukan sesuatu yang akan diblokir, seperti I / O) dapat menghabiskan banyak waktu prosesor melakukan pengalihan konteks. Ini terutama benar jika operasi pemblokiran Anda jarang memblokir terlalu lama.
Ada beberapa situasi di mana rute koperasi lebih masuk akal. Saya pernah harus menulis beberapa perangkat lunak userland untuk perangkat keras yang mengalirkan banyak saluran data melalui antarmuka yang dipetakan memori yang membutuhkan polling. Setiap saluran adalah objek yang dibangun sedemikian rupa sehingga saya bisa membiarkannya berjalan sebagai utas atau berulang kali menjalankan satu siklus polling.
Kinerja versi multithreaded tidak baik sama sekali untuk alasan yang saya jelaskan di atas: setiap thread melakukan pekerjaan minimal dan kemudian menghasilkan CPU sehingga saluran lain dapat memiliki beberapa waktu, menyebabkan banyak saklar konteks. Membiarkan utas berjalan bebas sampai preempted membantu dengan throughput tetapi mengakibatkan beberapa saluran tidak diperbaiki sebelum perangkat keras mengalami buffer overrun karena mereka tidak mendapatkan waktu yang cukup cepat.
Versi single-threaded, yang melakukan iterasi pada setiap saluran, berjalan seperti kera yang tersiram air panas dan beban pada sistem turun seperti batu. Hukuman yang saya bayarkan untuk kinerja tambahan adalah harus menyulap tugas sendiri. Dalam hal ini, kode untuk melakukannya cukup sederhana sehingga biaya pengembangan dan pemeliharaannya sepadan dengan peningkatan kinerja. Saya kira itu benar-benar intinya. Seandainya utas saya adalah orang-orang yang duduk menunggu untuk panggilan sistem kembali, latihan mungkin tidak akan sepadan.
Itu membuat saya komentar Cox: utas tidak hanya untuk orang-orang yang tidak bisa menulis mesin negara. Beberapa orang cukup mampu melakukan itu tetapi memilih untuk menggunakan mesin kalengan (yaitu, utas) dengan tujuan menyelesaikan pekerjaan lebih cepat atau dengan kompleksitas yang lebih sedikit.
sumber
Yah aku jujur tidak bisa membayangkan bagaimana menangani memblokir I / O tanpa utas. Ini disebut blocking afterall hanya karena kode yang memanggilnya harus
wait
.Per saya membaca email asli Cox '(di bawah) dia menunjukkan meskipun threading itu tidak baik skala. Maksud saya, bagaimana jika ada 100 permintaan I / O? 1000? 10000? Cox menunjukkan bahwa memiliki sejumlah besar thread dapat menyebabkan masalah parah:
sumber: Re: Analisis menarik threading kernel linux oleh IBM (arsip mailing list linux-kernel)
sumber
Secara teori, ini benar. Dalam kehidupan nyata, utas hanyalah abstraksi efisien yang digunakan untuk memprogram mesin keadaan seperti itu. Mereka sangat efisien sehingga mereka dapat digunakan untuk memprogram Statecharts dan Petri nets juga (yaitu, perilaku paralel, di mana mesin negara pada dasarnya berurutan).
Masalah dengan mesin negara adalah ledakan kombinatorial. Jumlah status komputer dengan RAM 4G adalah status 2 ^ (2 ^ 32) (tidak termasuk drive disk 2T).
Bagi seorang pria yang satu-satunya alat adalah palu, setiap masalah terlihat seperti paku.
sumber
Utas adalah satu-satunya opsi dalam dua kasus:
Yang kedua adalah mengapa kebanyakan orang berpikir bahwa utas tidak dapat dihindari untuk melakukan IO atau pemrograman jaringan, tetapi ini biasanya karena mereka tidak tahu OS mereka memiliki API yang lebih maju (atau tidak ingin berkelahi dengan menggunakannya).
Adapun kemudahan penggunaan dan keterbacaan, selalu ada loop acara (seperti libev atau EventMachine ) yang membuat pemrograman mesin negara hampir sesederhana melakukannya dengan utas, namun memberikan kontrol yang cukup untuk melupakan masalah sinkronisasi.
sumber
Cara yang baik untuk grok cara mesin negara dan multithreading berinteraksi adalah dengan melihat event handler GUI. Banyak aplikasi / kerangka kerja GUI menggunakan utas GUI tunggal yang akan melakukan polling terhadap sumber input yang mungkin dan memanggil fungsi untuk setiap input yang diterima; pada dasarnya, ini bisa ditulis sebagai saklar besar:
Sekarang, menjadi cukup jelas bahwa tingkat kontrol tingkat tinggi dalam konstruksi ini tidak boleh tinggi: Pawang untuk ButtonPressed harus selesai tanpa interaksi pengguna dan kembali ke loop utama, karena jika tidak, tidak ada acara pengguna lebih lanjut dapat diproses. Jika memiliki status untuk disimpan, status ini harus disimpan dalam variabel global atau statis, tetapi tidak di tumpukan; artinya, aliran kontrol normal dalam bahasa imperatif terbatas. Anda pada dasarnya terbatas pada mesin negara.
Ini bisa menjadi sangat berantakan ketika Anda memiliki subrutin bersarang yang harus disimpan, misalnya, tingkat rekursi. Atau sedang membaca file, tetapi file tersebut tidak tersedia saat ini. Atau hanya dalam perhitungan panjang. Dalam semua kasus ini, diinginkan untuk menyimpan status eksekusi saat ini dan kembali ke loop utama, dan ini multithreading . Tidak lebih, tidak kurang.
Semuanya menjadi sedikit lebih berbelit-belit dengan pengenalan multithreading preemptive (yaitu sistem operasi memutuskan kapan thread harus menghasilkan kontrol), dan itulah sebabnya koneksi tidak segera jelas hari ini.
Jadi, untuk menjawab pertanyaan terakhir: Ya, mesin negara adalah alternatif, sebagian besar GUI bekerja seperti itu dengan utas GUI. Hanya saja, jangan mendorong mesin keadaan terlalu jauh, itu menjadi sangat sulit dipulihkan.
sumber
Bertanya apakah menggunakan mesin negara layak dalam bahasa tingkat tinggi sama seperti menanyakan apakah menulis dalam assembler adalah alternatif yang layak untuk menggunakan bahasa tingkat tinggi. Mereka berdua memiliki tempat masing-masing, mengingat situasi yang tepat.
Abstraksi menggunakan threading membuat sistem paralel yang lebih kompleks lebih mudah diimplementasikan, tetapi pada akhirnya semua sistem paralel memiliki masalah yang sama untuk dihadapi. Masalah klasik seperti Deadlock / Livelock dan inversi prioritas sama mungkin dengan sistem berbasis mesin negara seperti halnya dengan memori paralel , NUMA atau bahkan sistem berbasis CSP , jika cukup kompleks.
sumber
Saya tidak berpikir itu - pasti, mesin negara adalah konsep komputasi yang sangat 'elegan' tetapi seperti yang Anda katakan, mereka cukup rumit. Dan hal-hal rumit sulit untuk diperbaiki. Dan hal-hal yang tidak benar rusak begitu saja, jadi kecuali jika Anda seorang jenius yang dianggap perawakan Alan Cox, tetaplah dengan hal-hal yang Anda tahu berhasil - biarkan 'pengkodean pintar' untuk proyek pembelajaran.
Anda dapat mengetahui bahwa seseorang telah melakukan upaya sia-sia untuk melakukannya, karena (dengan asumsi itu bekerja dengan baik) ketika datang untuk mempertahankannya, Anda menemukan bahwa tugas tersebut hampir tidak mungkin. 'Jenius' asli akan pindah meninggalkan Anda dengan sejumlah kode yang hampir tidak dapat dipahami (karena jenis pengembang ini cenderung tidak meninggalkan terlalu banyak komentar apalagi dokumentasi teknis).
Beberapa kasus, mesin negara akan menjadi pilihan yang lebih baik - Saya sedang memikirkan jenis barang tertanam sekarang di mana beberapa pola mesin negara digunakan, dan digunakan berulang kali dan dengan cara yang lebih formal (yaitu teknik yang tepat :))
Threading juga bisa sulit untuk dilakukan dengan benar, tetapi ada pola untuk membantu Anda - terutama dengan mengurangi kebutuhan untuk berbagi data di antara utas.
Poin terakhir tentang ini adalah bahwa komputer modern berjalan pada banyak core, jadi mesin negara tidak akan benar-benar memanfaatkan sumber daya yang tersedia. Threading dapat melakukan pekerjaan yang lebih baik di sini.
sumber
Contoh yang bagus dari penggunaan mesin keadaan yang tepat sebagai ganti utas: nginx vs apache2. Secara umum Anda dapat mengasumsikan bahwa nginx menangani semua koneksi dalam satu utas, apache2 membuat utas per koneksi.
Tetapi bagi saya menggunakan mesin negara vs threads sangat mirip menggunakan asm vs java dibuat dengan sempurna: Anda dapat mencapai hasil yang lebih tinggi, tetapi dibutuhkan banyak upaya programmer, banyak disiplin, membuat proyek lebih kompleks dan bernilai hanya jika digunakan oleh banyak programmer lainnya. Jadi, jika Anda adalah orang yang ingin membuat server web cepat - gunakan mesin negara dan IO async. Jika Anda menulis proyek (bukan perpustakaan untuk digunakan di mana-mana) - gunakan utas.
sumber