Mesin negara vs utas

24

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?

Victor Sorokin
sumber
4
Komputer juga merupakan mesin Turing. Meskipun demikian, itu tidak selalu berguna untuk memprogramnya seperti mesin Turing. Tumpukan dalam bahasa imperatif adalah hal yang sangat berguna, dan program multithread memungkinkan untuk menyimpan banyak tumpukan di memori secara bersamaan. Melakukan hal yang sama di mesin negara pasti mungkin, tetapi semua lebih berantakan.
thiton
10
Alan adalah pengembang kernel OS; ini domiannya . Jadi kutipannya harus diambil dalam konteks itu. Dia akan memprogram 'melawan logam' di mana lebih masuk akal untuk menggunakan model seperti itu. Setelah OS memisahkan perangkat keras, dan properti intrinsiknya (bahwa "komputer adalah mesin negara ..."), Anda memiliki peluang dan manfaat untuk dapat menggunakan model lain yang lebih masuk akal di domain Anda . Hampir setiap game banyak menggunakan mesin negara.
Steven Evers
2
Threading hanyalah fitur OS untuk secara otomatis mengelola beberapa sakelar mesin negara Anda, jika Anda mau. Jelas Anda dapat membuat mesin negara besar yang akan mengatur semuanya sendiri, tapi itu lebih rumit. Hal yang sama dapat dikatakan tentang proses. Anda dapat mengatakan bahwa proses adalah untuk orang-orang yang tidak dapat memprogram mesin negara juga. Tetapi abstraksi ini memberi Anda antarmuka yang jauh lebih sederhana dan tidak terlalu rentan kesalahan. Menurut pendapat saya ini hanyalah "kutipan keren" yang harus didengar, direnungkan, dan kemudian diabaikan dalam kenyataan.
Yam Marcovic
2
"Tapi abstraksi [utas] ini memberi Anda antarmuka yang jauh lebih sederhana dan tidak terlalu rentan kesalahan." Tampaknya itu salah. Jumlah orang yang mendapatkan keselamatan ulir yang salah mengindikasikan bahwa tampaknya hal itu menyebabkan kesalahan.
S.Lott
1
Banyak komentar dan jawaban di sini menafsirkan kutipan sebagai anti-multitasking secara umum; Saya percaya Alan Cox hanyalah anti-utas dan akan mengadvokasi menggunakan banyak proses untuk mencapai banyak tujuan yang digunakan orang untuk utas. Ingatlah bahwa dia adalah peretas Unix: fork FTW. Saya belum menemukan komentar darinya langsung pada kutipan, tapi ini satu dari Larry McVoy dari mailing list kernel Linux yang masuk ke arah ini: lkml.indiana.edu/hypermail/linux/kernel/0106.2/0405.html
Martin B

Jawaban:

25

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.

  • T1-sebelum mengunci
  • T1-dengan kunci
  • T1-setelah kunci
  • T2-sebelum mengunci
  • T2-dengan kunci
  • T2-setelah kunci

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

S.Lott
sumber
Terima kasih, penjelasan yang bagus. Dan bagaimana dengan mesin multi-core? Saya kira, tidak ada cara eksplisit untuk mengeksploitasi core di mesin negara Jawa? Orang perlu mengandalkan OS untuk itu, kan?
Victor Sorokin
5
Mesin multi-core membuat penjadwalan sedikit lebih kompleks. Namun, semua core menulis ke memori umum tunggal, sehingga Memory Write Ordering di antara dua core berarti bahwa - pada dasarnya - kita kembali ke eksekusi memori yang disisipkan. OS mengeksploitasi core dan JVM memanfaatkan ini. Tidak perlu berpikir dua kali tentang hal itu.
S.Lott
9

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.

SF.
sumber
Kesalahan halaman kode dalam program eksekusi konteks tunggal pada dasarnya benar-benar menghalangi. Menurut definisi, kode yang memiliki konteks eksekusi tunggal tidak dapat membuat kemajuan maju sampai potongan kode berikutnya dalam aliran tersedia.
David Schwartz
1
@ David Schwartz: benar, itu pada dasarnya menghalangi; tapi itu bukan 'operasi' karena itu bukan sesuatu yang dilakukan oleh kode yang diblokir, itu adalah sesuatu yang terjadi padanya.
Javier
1
Pembacaan file tidak secara mendasar memblokir - itu selalu dapat dipecah menjadi meminta pembacaan lokasi yang diberikan dan memperoleh data dari buffer setelah permintaan dipenuhi. Dan kesalahan halaman adalah solusi untuk penggunaan swap serampangan / heuristik. Ini akan terjadi jika keadaan yang diberikan dimasukkan sebelum semua data yang diperlukan untuk pelaksanaannya tersedia - kurangnya pandangan ke depan, sesuatu yang bertentangan dengan konsep mesin negara. Jika operasi swap-out dan swap-in adalah bagian dari mesin negara, maka kesalahan halaman tidak akan terjadi.
SF.
1
@ David Schwartz: Definisi perilaku "memblokir" selalu tunduk pada persyaratan "waktu nyata". Misalnya: Kesalahan halaman kode dianggap non-pemblokiran untuk aplikasi yang membutuhkan respons dalam urutan ratusan milidetik. Di sisi lain jika aplikasi memiliki persyaratan realtime yang ketat, itu tidak akan menggunakan memori virtual sama sekali.
MaR
1
@Mar: ... atau gunakan algoritme swap deterministik yang menjamin pengambilan data yang diperlukan sebelum diperlukan.
SF.
9

Bagaimana cara 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?

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.

Apakah menggunakan "hanya mesin negara" sebagai alternatif yang layak untuk multi-threading dalam bahasa tingkat tinggi?

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.

Blrfl
sumber
2

bagaimana jika ada 2 kegiatan untuk dilakukan (melakukan perhitungan dan melakukan I / O) dan satu kegiatan dapat diblokir?

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:

Dari: Alan Cox ([email protected])
Tanggal: Jum 21 Jan 2000 - 13:33:52 EST

makalah IBM), bahwa jika aplikasi Anda bergantung pada sejumlah besar utas, Anda akan selalu bertemu dengan penjadwal? banyak orang melemparkan banyak utas pada suatu masalah dan itu bisa menjadi desain yang buruk.

Itu adalah yang paling tidak Anda khawatirkan. 1000 utas adalah 8Mb tumpukan kernel, dan cukup pengalihan tugas untuk memastikan Anda juga mematikan sebagian besar cache Anda. Komputer adalah mesin negara. Utas adalah untuk orang yang tidak dapat memprogram mesin negara.

Ada banyak kasus Linux yang paling pasti tidak membantu situasi terutama asinkron blok I / O.

Alan

sumber: Re: Analisis menarik threading kernel linux oleh IBM (arsip mailing list linux-kernel)

agas
sumber
1
  • 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.

mouviciel
sumber
1

Utas adalah satu-satunya opsi dalam dua kasus:

  • untuk menggunakan beberapa core tanpa pemisahan memori.
  • untuk mengatasi kode eksternal yang memblokir.

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.

mbq
sumber
1
Yang terbaik dari kedua dunia: Mesin negara kecil yang menghalangi dalam utas membuat kode aplikasi yang sangat sederhana. Dan utas-utas berpisah dengan baik jika Anda memilikinya. Jika semuanya tidak memiliki setidaknya dua core, maka akan segera. yaitu ponsel berbasis quad core arm yang datang tahun 2012.
Tim Williscroft
0

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:

while (true) {
    switch (event) {
        case ButtonPressed:
        ...
        case MachineIsBurning:
        ....
    }
}

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.

thiton
sumber
0

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.

Mark Booth
sumber
0

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.

gbjbaanb
sumber
2
Mesin negara sama sekali tidak rumit! Mesin state yang rumit itu rumit, tetapi begitu pula semua sistem yang kompleks: o)
MaR
2
-1 untuk "jangan coba-coba". itu saran terburuk yang bisa Anda berikan.
Javier
1
-1 "Jangan coba"? Itu bodoh sekali. Saya juga akan menantang pernyataan Anda bahwa mesin negara sulit. Setelah Anda masuk ke sesuatu seperti Heirarchal Fuzzy State Machine ... lalu ya, itu sedikit lebih rumit. Tapi mesin negara yang sederhana? Itu hal-hal mendasar yang dipelajari setiap tahun ke-2 ketika saya masih di sekolah.
Steven Evers
biarkan saya ulangi sedikit 'jangan coba' ...
gbjbaanb
0

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.

ZeusTheTrueGod
sumber