Perbedaan utama, seperti yang Anda tunjukkan dalam pertanyaan Anda, adalah apakah penjadwal akan mendahului utas. Cara seorang programmer berpikir tentang berbagi struktur data atau tentang sinkronisasi antara "utas" sangat berbeda dalam sistem preemptive dan kooperatif.
Dalam sistem kooperatif (yang dikenal dengan banyak nama, kooperatif multi-tasking , multi-tasking nonpreemptive , level pengguna , benang hijau , dan serat adalah lima yang umum saat ini) programmer dijamin bahwa kode mereka akan berjalan secara atom selama mereka tidak melakukan panggilan sistem atau panggilan apa pun yield()
. Ini membuatnya sangat mudah untuk berurusan dengan struktur data yang dibagi di antara banyak serat. Kecuali jika Anda perlu membuat panggilan sistem sebagai bagian dari bagian kritis, bagian penting tidak perlu ditandai (dengan mutex lock
dan unlock
panggilan, misalnya). Jadi dalam kode seperti:
x = x + y
y = 2 * x
programmer tidak perlu khawatir bahwa serat lain dapat bekerja dengan x
dan y
variabel - variabel pada saat yang sama. x
dan y
akan diperbarui bersama secara atomik dari perspektif semua serat lainnya. Demikian pula, semua serat dapat berbagi beberapa struktur yang lebih rumit, seperti pohon dan panggilan seperti tree.insert(key, value)
tidak perlu dilindungi oleh mutex atau bagian kritis.
Sebaliknya, dalam sistem multithreading preemptive, seperti dengan thread paralel / multicore yang benar-benar, setiap kemungkinan interleaving instruksi antara thread dimungkinkan kecuali ada bagian kritis yang eksplisit. Interupsi dan preemption bisa terjadi antara dua instruksi. Dalam contoh di atas:
thread 0 thread 1
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
read y
< thread 1 could read or modify x or y at this point
add x and y
< thread 1 could read or modify x or y at this point
write the result back into x
< thread 1 could read or modify x or y at this point
read x
< thread 1 could read or modify x or y at this point
multiply by 2
< thread 1 could read or modify x or y at this point
write the result back into y
< thread 1 could read or modify x or y at this point
Jadi, agar benar pada sistem preemptive, atau pada sistem dengan utas yang benar-benar paralel, Anda harus mengelilingi setiap bagian penting dengan semacam sinkronisasi, seperti mutex lock
di awal dan mutex unlock
di akhir.
Serat dengan demikian lebih mirip dengan pustaka i / o asinkron daripada dibandingkan dengan thread preemptive atau thread yang benar-benar paralel. Penjadwal serat dipanggil dan dapat mengganti serat selama operasi latensi panjang i / o. Ini dapat memberikan manfaat beberapa operasi i / o simultan tanpa memerlukan operasi sinkronisasi di sekitar bagian-bagian penting. Jadi, menggunakan serat dapat, mungkin, memiliki kompleksitas pemrograman yang lebih sedikit daripada benang preemptive atau benar-benar paralel, tetapi kurangnya sinkronisasi di sekitar bagian-bagian penting akan menyebabkan hasil yang merusak jika Anda mencoba menjalankan serat dengan benar secara simultan atau preemptive.
Jawabannya sebenarnya adalah mereka bisa, tetapi ada keinginan untuk tidak melakukannya.
Serat digunakan karena memungkinkan Anda mengontrol bagaimana penjadwalan terjadi. Dengan demikian, jauh lebih mudah untuk merancang beberapa algoritma menggunakan serat karena programmer telah mengatakan di mana serat dieksekusi pada suatu waktu. Namun, jika Anda ingin dua serat dieksekusi pada dua inti yang berbeda secara bersamaan, Anda harus menjadwalkannya secara manual.
Utas memberi kendali kode mana yang dieksekusi ke OS. Sebagai gantinya, OS menangani banyak tugas buruk untuk Anda. Beberapa algoritma menjadi lebih sulit, karena programmer memiliki lebih sedikit suara mengenai kode mana yang dieksekusi pada waktu tertentu, sehingga lebih banyak kasus yang tidak terduga dapat muncul. Alat-alat seperti mutex dan semaphore ditambahkan ke OS untuk memberikan programmer kontrol yang cukup untuk membuat utas bermanfaat dan mengalahkan beberapa ketidakpastian, tanpa menghalangi programmer.
Ini mengarah ke sesuatu yang bahkan lebih penting daripada kooperatif vs preemptive: serat dikontrol oleh programmer, sedangkan utas dikontrol oleh OS.
Anda tidak ingin harus menelurkan serat pada prosesor lain. Perintah tingkat perakitan untuk melakukannya sangat rumit, dan sering kali khusus untuk prosesor. Anda tidak ingin harus menulis 15 versi kode yang berbeda untuk menangani prosesor ini, jadi Anda beralih ke OS. Tugas OS adalah untuk memisahkan perbedaan-perbedaan ini. Hasilnya adalah "utas."
Serat berjalan di atas utas. Mereka tidak lari sendiri. Dengan demikian, jika Anda ingin menjalankan dua serat pada inti yang berbeda, Anda dapat memunculkan dua benang, dan menjalankan serat pada masing-masingnya. Dalam banyak penerapan serat, Anda dapat melakukan ini dengan mudah. Dukungan multi-core tidak berasal dari serat, tetapi dari benang.
Menjadi mudah untuk menunjukkan bahwa, kecuali jika Anda ingin menulis kode spesifik prosesor Anda sendiri, tidak ada yang dapat Anda lakukan dengan menetapkan serat ke beberapa inti yang tidak dapat Anda lakukan dengan membuat utas dan menetapkan serat ke masing-masing. Salah satu aturan favorit saya untuk desain API adalah "API tidak dilakukan ketika Anda telah selesai menambahkan semuanya, melainkan ketika Anda tidak lagi dapat menemukan hal lain untuk dihapus." Mengingat multi-core ditangani dengan sempurna oleh hosting serat pada utas, tidak ada alasan untuk menyulitkan API serat dengan menambahkan multi-inti pada tingkat itu.
sumber