Concurrency: Bagaimana Anda mendekati desain dan men-debug implementasi?

37

Saya telah mengembangkan sistem konkuren selama beberapa tahun sekarang, dan saya memiliki pemahaman yang cukup baik tentang masalah ini meskipun saya kurang pelatihan formal (yaitu tidak ada gelar). Ada beberapa bahasa baru yang telah menjadi populer untuk setidaknya dibicarakan akhir-akhir ini yang dirancang untuk membuat konkurensi lebih mudah seperti Erlang dan Go. Tampaknya pendekatan mereka terhadap konkurensi menggemakan pengalaman saya sendiri tentang bagaimana membuat sistem terukur dan mengambil keuntungan dari beberapa core / prosesor / mesin.

Namun, saya menemukan bahwa ada sangat sedikit alat untuk membantu memvisualisasikan apa yang ingin Anda lakukan, dan memverifikasi bahwa Anda setidaknya dekat dengan visi asli Anda. Kode bersamaan debug dapat menjadi mimpi buruk dengan bahasa yang tidak dirancang untuk konkurensi (seperti C / C ++, C #, Java, dll.). Secara khusus, hampir tidak mungkin menciptakan kembali kondisi yang terjadi dengan mudah pada satu sistem di lingkungan pengembangan Anda.

Jadi, apa pendekatan Anda untuk merancang sistem untuk menangani pemrosesan konkurensi dan paralel? Contoh:

  • Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan vs apa yang harus dilakukan secara berurutan?
  • Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?
  • Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Saya punya jawaban sendiri untuk beberapa di antaranya, tetapi saya juga ingin belajar sedikit lebih banyak.

Edit

Sejauh ini kami memiliki banyak masukan yang bagus. Banyak artikel yang terhubung sangat bagus, dan saya sudah membaca beberapa di antaranya.

Pengalaman pribadi saya dengan pemrograman konkuren membuat saya percaya bahwa Anda membutuhkan pola pikir yang berbeda dari yang Anda lakukan dengan pemrograman berurutan. Kesenjangan mental mungkin selebar perbedaan antara pemrograman berorientasi objek dan pemrograman prosedural. Saya ingin serangkaian pertanyaan ini untuk lebih fokus pada proses pemikiran yang diperlukan (yaitu teori) untuk secara sistematis mendekati jawaban. Ketika memberikan jawaban yang lebih konkret, ada baiknya memberikan contoh - sesuatu yang Anda alami secara pribadi.

Sasaran untuk Bounty

Jangan katakan padaku apa yang harus aku lakukan. Saya sudah mengendalikannya. Katakan apa yang kamu lakukan. Ceritakan bagaimana Anda memecahkan masalah ini.

Berin Loritsch
sumber
Ini adalah pertanyaan yang bagus - banyak kedalaman yang mungkin. Saya juga mendapatkan pengalaman yang bagus dengan aplikasi multi-threaded di Java, tetapi ingin mempelajari lebih lanjut.
Michael K
Sejauh ini, kami punya beberapa jawaban bagus. Adakah yang mau mencoba apa yang Anda inginkan untuk membantu Anda di bidang ini?
Berin Loritsch
TotalView Debugger untuk pengkodean konkuren adalah alat yang sangat berguna, meskipun memerlukan sedikit kurva pembelajaran - totalviewtech.com/products/totalview.html
Fanatic23
Mungkin logging dapat membantu Anda dengan dua pertanyaan terakhir.
Amir Rezaei
Yang saya cari adalah proses orang. Ini adalah area di mana alat yang saya gunakan tidak memadai, tetapi bisa menyelesaikan pekerjaan. Saya kurang khawatir tentang mengutip artikel orang lain dan lebih peduli tentang metodologi di sini.
Berin Loritsch

Jawaban:

11

Saya telah mengembangkan sistem konkuren selama beberapa tahun sekarang, dan saya memiliki pemahaman yang cukup baik tentang masalah ini meskipun saya tidak memiliki pelatihan formal (yaitu tidak ada gelar).

Banyak programmer terbaik yang saya tahu tidak menyelesaikan Universitas. Sedangkan saya, saya belajar Filsafat.

C / C ++, C #, Java, dll.). Secara khusus, hampir tidak mungkin menciptakan kembali kondisi yang terjadi dengan mudah pada satu sistem di lingkungan pengembangan Anda.

iya nih

Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan vs apa yang harus dilakukan secara berurutan?

kita biasanya mulai dengan metafora 1000 mil untuk memperjelas arsitektur kita kepada diri kita sendiri (pertama) dan kepada orang lain (kedua).

Ketika kami menghadapi masalah itu, kami selalu menemukan cara untuk membatasi visibilitas objek konkuren menjadi yang non konkuren.

Akhir-akhir ini saya menemukan Aktor di scala dan saya melihat bahwa solusi lama saya adalah semacam "miniactors", jauh lebih kuat daripada yang scala. Jadi saran saya adalah mulai dari sana.

Saran lain adalah untuk melewati sebanyak mungkin masalah: misalnya kita menggunakan cache terpusat (terakota) alih-alih menyimpan peta dalam memori, menggunakan panggilan balik kelas dalam alih-alih metode yang disinkronkan, mengirim pesan alih-alih menulis memori bersama, dll.

Dengan scala, itu semua jauh lebih mudah.

Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?

Tidak ada jawaban nyata di sini. Kami memiliki beberapa unit test untuk konkurensi dan kami memiliki load test suite untuk menekankan aplikasi sebanyak yang kami bisa.

Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Sekali lagi tidak ada jawaban nyata: kami mendesain Metafora kami di papan tulis dan kami berusaha memastikan tidak ada konflik di sisi arsitektur.

Untuk Arch di sini yang saya maksud adalah definisi Neal Ford: Arsitektur Sw adalah segalanya yang akan sangat sulit untuk diubah nanti.

pemrograman membuat saya percaya bahwa Anda memerlukan pola pikir yang berbeda dari yang Anda lakukan dengan pemrograman berurutan.

Mungkin tetapi bagi saya itu tidak mungkin untuk berpikir secara paralel, jadi lebih baik rancang perangkat lunak kami dengan cara yang tidak memerlukan pemikiran paralel dan dengan pagar yang jelas untuk menghindari tabrakan antara jalur konkurensi.

Uberto
sumber
6

Bagi saya adalah semua tentang data. Hancurkan data Anda dengan benar, dan pemrosesan paralelnya mudah. Semua masalah dengan retensi, kebuntuan, dan sebagainya hilang.

Saya tahu bahwa ini bukan satu-satunya cara untuk memparalelkan, tetapi bagi saya adalah yang paling berguna.

Sebagai ilustrasi, cerita (tidak terlalu cepat):

Saya memang bekerja pada sistem keuangan besar (kontrol pasar saham) pada 2007 hingga 2009, dan volume pemrosesan data sangat besar. Sebagai ilustrasi, semua perhitungan yang dilakukan untuk 1 akun tunggal klien membutuhkan waktu sekitar 1 ~ 3 detik pada stasiun kerja rata-rata mereka, dan ada lebih dari 30k akun. Setiap malam, menutup sistem adalah masalah besar bagi para pengguna (biasanya lebih dari 6 jam pemrosesan, tanpa margin kesalahan untuk mereka).

Mempelajari masalah lebih lanjut mengungkapkan bahwa kami dapat memparalelkan perhitungan di antara beberapa komputer, tetapi kami masih akan mengalami hambatan besar pada server database lama (server SQL 2000 meniru SQL 6.5).

Sudah cukup jelas bahwa paket pemrosesan minimum kami adalah perhitungan satu akun, dan hambatan utama adalah retensi server database (kita bisa melihat pada beberapa koneksi "sp_who" yang menunggu untuk melakukan pemrosesan yang sama). Jadi proses paralelnya berjalan seperti ini:

1) Satu produser tunggal, yang bertanggung jawab untuk membaca database atau menulisnya, secara berurutan. Konkurensi tidak diizinkan di sini. Produser menyiapkan antrian pekerjaan, untuk konsumen. Basis data hanya milik produsen ini.

2) Beberapa konsumen, pada beberapa mesin. Masing-masing konsumen menerima seluruh paket data, dari antrian, siap untuk dihitung. Setiap operasi deqeue disinkronkan.

3) Setelah perhitungan, setiap konsumen mengirim kembali data ke dalam antrian tersinkronkan dalam memori ke produsen, untuk mempertahankan data.

Ada beberapa titik pemeriksaan, beberapa mekanisme untuk memastikan transaksi telah disimpan dengan benar (tidak ada yang tertinggal), tetapi seluruh pekerjaan itu layak dilakukan. Pada akhirnya, perhitungan menyebar di antara 10 komputer (ditambah komputer produsen / antrian) mencatat waktu penutupan seluruh sistem menjadi 15 menit.

Hanya menghilangkan masalah retensi yang disebabkan oleh manajemen konkurensi yang buruk SQL 6.5 telah memberi kami keuntungan besar. Sisanya cukup linier, setiap komputer baru ditambahkan ke "grid" membuat waktu pemrosesan turun, sampai kami mencapai "efisiensi maksimum" dari operasi baca / tulis berurutan pada database.

Machado
sumber
2

Bekerja di lingkungan multi-threading sulit dan membutuhkan disiplin koding. Anda harus mengikuti panduan yang tepat untuk mengambil kunci, melepaskan kunci, mengakses variabel global dll.

Biarkan saya mencoba menjawab pertanyaan Anda satu per satu

* How do you figure out what can be made concurrent vs. what has to be sequential?

Gunakan konkurensi untuk

1) Polling: - butuh utas untuk terus-menerus polling sesuatu atau mengirim pembaruan secara teratur. (Konsep seperti heart-bit, yang mengirimkan beberapa data secara berkala ke server pusat untuk mengatakan bahwa saya masih hidup.)

2) Operasi yang memiliki berat i / o dapat dibuat paralel. Contoh terbaik adalah logger. Thread logger bisa menjadi utas terpisah.

3) Tugas serupa pada data yang berbeda. Jika ada beberapa tugas yang terjadi pada data yang berbeda tetapi sifatnya sangat mirip, utas yang berbeda dapat melakukan ini. Contoh terbaik adalah permintaan server.

Dan tentunya banyak yang lain seperti ini tergantung aplikasi.

* How do you reproduce error conditions and view what is happening as the application executes?

Menggunakan log dan debug cetakan dalam log. Cobalah untuk login juga id utas sehingga Anda dapat melihat apa yang terjadi di setiap utas.
Salah satu cara untuk menghasilkan kondisi kesalahan adalah dengan meletakkan penundaan yang disengaja (dalam kode debug) di tempat-tempat di mana Anda berpikir masalah tersebut terjadi, dan dengan paksa menghentikan utas itu. Hal-hal serupa juga dapat dilakukan di debuggers, tetapi saya belum melakukannya sejauh ini.

* How do you visualize the interactions between the different concurrent parts of the application?

Masukkan log di kunci Anda, sehingga Anda akan tahu siapa yang mengunci apa dan kapan, dan siapa yang telah mencoba untuk kunci. Seperti yang saya katakan sebelumnya mencoba untuk meletakkan id utas dalam log untuk memahami apa yang terjadi di setiap utas.

Ini hanya saran saya yaitu sekitar 3 tahun bekerja pada aplikasi multithread, dan semoga membantu.

Manoj R
sumber
2
  • Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan vs apa yang harus dilakukan secara berurutan?

Saya akan bertanya dulu apakah aplikasi (atau komponen) akan benar-benar melihat manfaat dari pemrosesan bersamaan, atau dalam istilah awam - di mana hambatannya? Concurrency jelas tidak akan selalu memberikan manfaat untuk investasi yang dibutuhkan untuk membuatnya bekerja. Jika itu terlihat seperti seorang kandidat, maka saya akan bekerja dari bawah ke atas - mencoba untuk menemukan operasi terbesar atau serangkaian operasi yang dapat melakukan tugasnya secara efektif dalam isolasi - Saya tidak ingin memutar-mutar benang untuk tidak signifikan, tidak hemat biaya operasi - Saya mencari Aktor .

Bekerja dengan Erlang Saya benar-benar menyukai konsep menggunakan passing pesan tidak sinkron dan model aktor untuk konkurensi - ini intuitif, efektif, dan bersih.

Off of Understanding Actor Concurrency

Model aktor terdiri dari beberapa prinsip utama:

  • Tidak ada status bersama
  • Proses ringan
  • Berlalu pesan tidak sinkron
  • Kotak surat untuk buffer pesan masuk
  • Pemrosesan kotak surat dengan pencocokan pola

Seorang aktor adalah proses yang mengeksekusi fungsi. Di sini proses adalah thread ruang pengguna yang ringan (jangan dikacaukan dengan proses sistem operasi kelas berat yang umum). Aktor tidak pernah berbagi status dan karenanya tidak perlu bersaing untuk mendapatkan akses ke data bersama. Alih-alih, pelaku berbagi data dengan mengirim pesan yang tidak dapat diubah. Data yang tidak dapat diubah tidak dapat dimodifikasi, jadi bacaan tidak memerlukan kunci.

Model konkurensi Erlang lebih mudah dipahami dan di-debug daripada mengunci dan berbagi data. Cara logika Anda diisolasi membuatnya mudah untuk melakukan pengujian komponen dengan mengirimkannya pesan.

Bekerja dengan sistem konkuren ini cukup banyak bagaimana desain saya tetap bekerja dalam bahasa apa pun - antrian yang menarik banyak utas data, melakukan operasi sederhana dan mengulang atau mendorong kembali ke antrian. Erlang hanya menegakkan struktur data yang tidak dapat diubah untuk mencegah efek samping dan mengurangi biaya dan kerumitan pembuatan utas baru.

Model ini bukan eksklusif Erlang, bahkan di dunia Java dan .NET ada cara untuk membuat ini - saya akan melihat Concurrency dan Coordination Runtime (CCR) dan Relang (ada juga Jetlang untuk Jawa).

  • Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?

Dalam pengalaman saya, satu-satunya hal yang dapat saya lakukan adalah diberikan komitmen untuk melacak / mencatat semuanya. Setiap proses / utas perlu memiliki pengidentifikasi dan setiap unit kerja baru harus memiliki id korelasi. Anda harus dapat melihat log Anda dan melacak apa yang sedang diproses dan kapan - tidak ada keajaiban yang saya lihat untuk menghilangkan ini.

  • Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Lihat di atas, itu jelek tapi berhasil. Satu-satunya hal lain yang saya lakukan adalah menggunakan diagram urutan UML - tentu saja ini selama waktu desain - tetapi Anda dapat menggunakannya untuk memverifikasi bahwa komponen Anda berbicara seperti yang Anda inginkan juga.

Watson
sumber
1

- Jawaban saya spesifik MS / Visual Studio -

Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan vs apa yang harus dilakukan secara berurutan?

Itu akan mengambil pengetahuan domain, tidak akan ada pernyataan selimut di sini untuk menutupinya.

Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?

Banyak penebangan, mampu menyalakan / mematikan log dalam aplikasi produksi untuk menangkapnya dalam produksi. VS2010 Intellitrace seharusnya dapat membantu dengan ini, tapi saya belum menggunakannya.

Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Saya tidak punya jawaban yang bagus untuk ini, akan senang melihatnya.

Es hitam
sumber
Logging akan mengubah bagaimana kode dieksekusi dan dengan demikian dapat menyebabkan kesalahan Anda setelah tidak muncul.
Matius Baca
1

Saya tidak setuju dengan pernyataan Anda bahwa C tidak dirancang untuk konkurensi. C dirancang untuk pemrograman sistem umum dan menikmati kegigihan untuk menunjukkan keputusan penting yang akan dibuat, dan akan terus melakukannya selama bertahun-tahun yang akan datang. Ini benar bahkan ketika keputusan terbaik mungkin tidak menggunakan C. Selain itu, konkurensi dalam C hanya sesulit desain Anda yang kompleks.

Saya mencoba, dengan kemampuan terbaik saya, untuk mengimplementasikan kunci dengan gagasan bahwa pada akhirnya, pemrograman bebas kunci yang benar - benar praktis mungkin menjadi kenyataan bagi saya. Dengan mengunci, maksud saya bukan pengecualian bersama, yang saya maksudkan hanyalah proses yang mengimplementasikan konkurensi aman tanpa perlu arbitrase. Secara praktis, maksud saya adalah sesuatu yang lebih mudah untuk di port daripada diimplementasikan. Saya memiliki sedikit pelatihan CS formal, tetapi saya kira saya diizinkan untuk berharap :)

Setelah itu, sebagian besar bug yang saya temui menjadi relatif dangkal, atau benar-benar membingungkan saya bahwa saya mundur ke sebuah pub. Pub menjadi pilihan yang menarik hanya ketika membuat profil program memperlambatnya cukup untuk mengekspos ras tambahan yang tidak terkait dengan apa yang saya coba temukan.

Seperti yang telah ditunjukkan orang lain, masalah yang Anda gambarkan sangat spesifik untuk domain. Saya hanya mencoba, dengan kemampuan terbaik saya untuk menghindari kasus apa pun yang mungkin memerlukan arbitrase (di luar proses saya) bila memungkinkan. Jika itu terlihat seperti rasa sakit yang agung, saya mengevaluasi kembali opsi untuk memberikan beberapa utas atau proses akses bersamaan dan tidak terverifikasi untuk sesuatu.

Kemudian lagi, membuang 'didistribusikan' di sana dan arbitrase menjadi suatu keharusan. Apakah Anda memiliki contoh spesifik?

Pos Tim
sumber
Untuk memperjelas pernyataan saya, C tidak dirancang khusus untuk dan di sekitar konkurensi. Ini berbeda dengan bahasa seperti Go, Erlang, dan Scala yang dirancang secara eksplisit dengan mempertimbangkan konkurensi. Saya tidak bermaksud mengatakan Anda tidak dapat melakukan konkurensi dengan C.
Berin Loritsch
1

Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?

Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Berdasarkan pengalaman saya, jawaban untuk dua aspek ini adalah sebagai berikut:

Pelacakan terdistribusi

Pelacakan terdistribusi adalah teknologi yang menangkap data pengaturan waktu untuk setiap komponen bersamaan dari sistem Anda, dan menyajikannya kepada Anda dalam format grafis. Representasi eksekusi bersamaan selalu disisipkan, memungkinkan Anda untuk melihat apa yang berjalan secara paralel dan apa yang tidak.

Pelacakan terdistribusi berawal pada (tentu saja) sistem terdistribusi, yang menurut definisi asinkron dan sangat konkuren. Sistem terdistribusi dengan penelusuran terdistribusi memungkinkan orang untuk:

a) mengidentifikasi kemacetan penting, b) mendapatkan representasi visual dari 'berjalan' ideal aplikasi Anda, dan c) memberikan visibilitas ke dalam perilaku konkuren apa yang sedang dieksekusi, d) memperoleh data waktu yang dapat digunakan untuk menilai perbedaan antara perubahan dalam Anda sistem (sangat penting jika Anda memiliki SLA yang kuat).

Namun, konsekuensi dari pelacakan yang didistribusikan adalah:

  1. Itu menambahkan overhead ke semua proses bersamaan Anda, karena diterjemahkan menjadi lebih banyak kode untuk mengeksekusi dan mengirimkan berpotensi melalui jaringan. Dalam beberapa kasus, overhead ini sangat signifikan - bahkan Google hanya menggunakan sistem penelusuran Dapper pada sebagian kecil dari semua permintaan agar tidak merusak pengalaman pengguna.

  2. Ada banyak alat yang berbeda, tidak semuanya saling beroperasi satu sama lain. Ini agak diperbaiki oleh standar seperti OpenTracing, tetapi tidak sepenuhnya diselesaikan.

  3. Ini memberitahu Anda apa-apa tentang sumber daya bersama dan status mereka saat ini. Anda mungkin bisa menebak, berdasarkan pada kode aplikasi dan grafik apa yang Anda lihat tunjukkan kepada Anda, tetapi itu bukan alat yang berguna dalam hal ini.

  4. Alat saat ini mengasumsikan Anda memiliki memori dan penyimpanan untuk cadangan. Hosting server jangka waktu mungkin tidak murah, tergantung pada kendala Anda.

Perangkat lunak pelacakan kesalahan

Saya menautkan ke Sentry di atas terutama karena itu adalah alat yang paling banyak digunakan di luar sana, dan untuk alasan yang bagus - perangkat lunak pelacakan kesalahan seperti eksekusi runtime Sentry membajak eksekusi secara bersamaan meneruskan tumpukan jejak kesalahan yang ditemui ke server pusat.

Manfaat bersih dari perangkat lunak khusus tersebut dalam kode bersamaan:

  1. Kesalahan duplikat tidak diduplikasi . Dengan kata lain, jika satu atau lebih sistem bersamaan menemukan pengecualian yang sama, Sentry akan menambah laporan insiden, tetapi tidak mengirimkan dua salinan dari insiden tersebut.

Ini berarti Anda dapat mengetahui sistem konkuren mana yang mengalami jenis kesalahan apa tanpa harus melalui laporan kesalahan simultan yang tak terhitung jumlahnya. Jika Anda pernah menderita spam email dari sistem terdistribusi, Anda tahu seperti apa rasanya.

Anda bahkan dapat 'menandai' aspek berbeda dari sistem konkuren Anda (meskipun ini mengasumsikan Anda tidak memiliki pekerjaan yang disisipkan tepat di atas satu utas, yang secara teknis tidak bersamaan juga karena utas tersebut hanya melompat di antara tugas-tugas secara efisien tetapi masih harus memproses penangan acara sampai selesai) dan melihat rincian kesalahan dengan tag.

  1. Anda dapat memodifikasi perangkat lunak penanganan kesalahan ini untuk memberikan detail tambahan dengan pengecualian runtime Anda. Sumber daya terbuka apa yang dimiliki proses ini? Apakah ada sumber daya bersama yang dimiliki proses ini? Pengguna mana yang mengalami masalah ini?

Ini, selain jejak tumpukan yang teliti (dan sumber peta, jika Anda harus menyediakan versi yang diperkecil dari file Anda), membuatnya mudah untuk menentukan apa yang salah sebagian besar dari waktu.

  1. (Khusus sentry) Anda dapat memiliki dasbor pelaporan Sentry yang terpisah untuk uji coba sistem, memungkinkan Anda menangkap kesalahan dalam pengujian.

Kerugian dari perangkat lunak tersebut meliputi:

  1. Seperti semuanya, mereka menambahkan massal. Anda mungkin tidak ingin sistem seperti itu pada perangkat keras yang disematkan, misalnya. Saya sangat merekomendasikan melakukan uji coba perangkat lunak tersebut, membandingkan eksekusi sederhana dengan dan tanpa itu sampel lebih dari beberapa ratus berjalan pada mesin idle.

  2. Tidak semua bahasa sama-sama didukung, karena banyak dari sistem ini mengandalkan secara implisit menangkap pengecualian dan tidak semua bahasa memiliki pengecualian yang kuat. Yang sedang berkata, ada klien untuk banyak sistem.

  3. Mereka mungkin dinaikkan sebagai risiko keamanan, karena banyak dari sistem ini pada dasarnya adalah sumber tertutup. Dalam kasus seperti itu, lakukan uji tuntas Anda dalam meneliti mereka, atau, jika lebih disukai, lakukan sendiri.

  4. Mereka mungkin tidak selalu memberi Anda informasi yang Anda butuhkan. Ini adalah risiko dengan semua upaya untuk menambah visibilitas.

  5. Sebagian besar layanan ini dirancang untuk aplikasi web yang sangat konkuren, jadi tidak setiap alat mungkin cocok untuk kasus penggunaan Anda.

Singkatnya : memiliki visibilitas adalah bagian paling penting dari sistem konkuren. Dua metode yang saya jelaskan di atas, bersama dengan dasbor khusus tentang perangkat keras dan data untuk mendapatkan gambaran holidtik sistem pada titik waktu tertentu, banyak digunakan di seluruh industri tepatnya untuk mengatasi aspek itu.

Beberapa saran tambahan

Saya telah menghabiskan lebih banyak waktu daripada saya peduli untuk memperbaiki kode oleh orang-orang yang mencoba untuk memecahkan masalah bersamaan dengan cara yang mengerikan. Setiap kali, saya telah menemukan kasus di mana hal-hal berikut dapat sangat meningkatkan pengalaman pengembang (yang sama pentingnya dengan pengalaman pengguna):

  • Mengandalkan tipe . Mengetik ada untuk memvalidasi kode Anda, dan dapat digunakan saat runtime sebagai penjaga tambahan. Di mana pengetikan tidak ada, mengandalkan penegasan dan penangan kesalahan yang sesuai untuk menangkap kesalahan. Kode bersamaan membutuhkan kode defensif, dan tipe berfungsi sebagai jenis validasi terbaik yang tersedia.

    • Tautan uji antara komponen kode , bukan hanya komponen itu sendiri. Jangan bingung ini dengan uji integrasi lengkap - yang menguji setiap tautan antara setiap komponen, dan bahkan saat itu hanya mencari validasi global dari kondisi akhir. Ini adalah cara yang mengerikan untuk menangkap kesalahan.

Tes tautan yang baik memeriksa untuk melihat apakah, ketika satu komponen berbicara ke komponen lain secara terpisah , pesan yang diterima dan pesan yang dikirim adalah sama seperti yang Anda harapkan. Jika Anda memiliki dua komponen atau lebih yang mengandalkan layanan bersama untuk berkomunikasi, putar semuanya, minta mereka bertukar pesan melalui layanan pusat, dan lihat apakah mereka semua mendapatkan apa yang Anda harapkan pada akhirnya.

Memecah pengujian yang melibatkan banyak komponen menjadi pengujian komponen itu sendiri dan pengujian tentang bagaimana masing-masing komponen berkomunikasi juga memberi Anda peningkatan kepercayaan diri terhadap validitas kode Anda. Memiliki tubuh pengujian yang ketat memungkinkan Anda untuk menegakkan kontrak antara layanan serta menangkap kesalahan tak terduga yang terjadi ketika mereka menjalankan sekaligus.

  • Gunakan algoritma yang tepat untuk memvalidasi keadaan aplikasi Anda. Saya berbicara tentang hal-hal sederhana, seperti ketika Anda memiliki proses master menunggu semua pekerjanya menyelesaikan tugas dan hanya ingin pindah ke langkah berikutnya jika semua pekerjanya selesai sepenuhnya - ini adalah contoh mendeteksi global terminasi, yang metodologi yang dikenal seperti algoritma Safra ada.

Beberapa alat ini dibundel dengan bahasa - Rust, misalnya, menjamin kode Anda tidak akan memiliki kondisi balapan pada waktu kompilasi, sementara Go memiliki detektor kebuntuan inbuilt yang juga berjalan pada waktu kompilasi. Jika Anda dapat menangkap masalah sebelum mencapai produksi, itu selalu merupakan kemenangan.

Aturan umum: desain untuk kegagalan dalam sistem bersamaan . Mengantisipasi bahwa layanan umum akan macet atau pecah. Ini berlaku bahkan untuk kode yang tidak didistribusikan di mesin - kode bersamaan pada satu mesin dapat mengandalkan dependensi eksternal (seperti file log bersama, server Redis, server MySQL sialan) yang dapat menghilang atau dihapus kapan saja .

Cara terbaik untuk melakukan ini adalah dengan memvalidasi status aplikasi dari waktu ke waktu - lakukan pemeriksaan kesehatan untuk setiap layanan, dan pastikan konsumen dari layanan tersebut diberitahu tentang kesehatan yang buruk. Alat kontainer modern seperti Docker melakukan ini dengan sangat baik, dan harus dimanfaatkan untuk hal-hal kotak pasir.

Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan dan apa yang bisa dibuat berurutan?

Salah satu pelajaran terbesar yang saya pelajari bekerja pada sistem yang sangat konkuren adalah ini: Anda tidak akan pernah memiliki metrik yang cukup . Metrik harus benar-benar mengarahkan semua hal dalam aplikasi Anda - Anda bukan seorang insinyur jika Anda tidak mengukur semuanya.

Tanpa metrik, Anda tidak dapat melakukan beberapa hal yang sangat penting:

  1. Nilai perbedaan yang dibuat oleh perubahan pada sistem. Jika Anda tidak tahu apakah tombol tuning A membuat metrik B naik dan metrik C turun, Anda tidak tahu cara memperbaiki sistem Anda ketika orang-orang mendorong kode ganas yang tidak terduga pada sistem Anda (dan mereka akan mendorong kode ke sistem Anda) .

  2. Pahami apa yang perlu Anda lakukan selanjutnya untuk meningkatkan hal-hal. Sampai Anda tahu aplikasi kehabisan memori, Anda tidak dapat membedakan apakah Anda harus mendapatkan lebih banyak memori atau membeli lebih banyak disk untuk server Anda.

Metrik sangat penting dan esensial sehingga saya telah berupaya secara sadar untuk merencanakan apa yang ingin saya ukur sebelum saya bahkan berpikir tentang apa yang dibutuhkan suatu sistem. Bahkan, metrik sangat penting sehingga saya percaya mereka adalah jawaban yang tepat untuk pertanyaan ini: Anda hanya tahu apa yang bisa dibuat berurutan atau bersamaan ketika Anda mengukur apa yang dilakukan bit dalam program Anda. Desain yang tepat menggunakan angka, bukan dugaan.

Yang sedang berkata, tentu ada beberapa aturan praktis:

  1. Berurutan menyiratkan ketergantungan. Dua proses harus berurutan jika satu bergantung pada yang lain dalam beberapa cara. Proses tanpa ketergantungan harus bersamaan. Namun, rencanakan cara untuk menangani kegagalan di hulu yang tidak mencegah proses hilir menunggu tanpa batas.

  2. Jangan pernah mencampur tugas yang terikat I / O dengan tugas yang terikat CPU pada inti yang sama. Jangan (misalnya) menulis perayap web yang meluncurkan sepuluh permintaan bersamaan di utas yang sama, mengikisnya segera setelah masuk, dan berharap untuk menskalakan hingga lima ratus - permintaan I / O menuju antrian secara paralel, tetapi CPU masih akan melalui mereka secara serial. (Model single-threaded ini didorong oleh model populer, tetapi terbatas karena aspek ini - daripada memahami ini, orang-orang hanya memeras tangan mereka dan mengatakan Node tidak skala, untuk memberi Anda contoh).

Satu utas dapat melakukan banyak pekerjaan I / O. Tetapi untuk sepenuhnya menggunakan konkurensi perangkat keras Anda, gunakan threadpools yang bersama-sama menempati semua inti. Dalam contoh di atas, meluncurkan lima proses Python (masing-masing dapat menggunakan inti pada mesin enam-inti) hanya untuk pekerjaan CPU dan utas keenam hanya untuk pekerjaan I / O akan skala jauh lebih cepat daripada yang Anda pikirkan.

Satu-satunya cara untuk memanfaatkan konkurensi CPU adalah melalui threadpool khusus. Satu utas seringkali cukup baik untuk banyak pekerjaan terikat I / O. Inilah sebabnya mengapa server web yang digerakkan oleh peristiwa seperti skala Nginx lebih baik (mereka benar-benar bekerja dengan I / O) daripada Apache (yang mengonfigurasi pekerjaan terikat I / O dengan sesuatu yang membutuhkan CPU dan meluncurkan proses per permintaan), tetapi mengapa menggunakan Node untuk melakukan puluhan ribu perhitungan GPU yang diterima secara paralel adalah ide yang buruk .

Akshat Mahajan
sumber
0

Nah, untuk proses verifikasi, ketika merancang sistem konkuren besar - saya cenderung menguji model menggunakan LTSA - Labeled Transition System Analyzer . Ini dikembangkan oleh tutor lama saya, yang merupakan seorang veteran di bidang concurrency dan sekarang adalah Kepala Komputer di Imperial.

Sejauh mengetahui apa yang bisa dan tidak bisa bersamaan, ada analisis statis yang dapat menunjukkan bahwa saya percaya, meskipun saya cenderung hanya menggambar diagram penjadwalan untuk bagian-bagian penting, sama seperti yang Anda lakukan untuk manajemen proyek. Kemudian identifikasi bagian yang melakukan operasi yang sama secara berulang. Rute cepat hanya untuk menemukan loop, karena mereka cenderung menjadi area yang mendapat manfaat dari pemrosesan paralel.

Orbling
sumber
0

Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan vs apa yang harus dilakukan secara berurutan?

Hampir setiap hal yang Anda tulis dapat memanfaatkan konkurensi terutama kasus penggunaan "devide an conquer". Pertanyaan yang jauh lebih baik adalah apa yang harus bersamaan?

Threading dalam daftar # # penggunaan umum Joseph Albahari .

Multithreading memiliki banyak kegunaan; di sini adalah yang paling umum:

Mempertahankan antarmuka pengguna yang responsif

Dengan menjalankan tugas yang menghabiskan waktu pada utas “pekerja” paralel, utas UI utama bebas untuk melanjutkan pemrosesan acara keyboard dan mouse.

Memanfaatkan CPU yang diblokir secara efisien

Multithreading berguna ketika utas sedang menunggu respons dari komputer lain atau perangkat keras. Sementara satu utas diblokir saat melakukan tugas, utas lain dapat memanfaatkan komputer yang tidak terbebani.

Pemrograman paralel

Kode yang melakukan perhitungan intensif dapat mengeksekusi lebih cepat pada komputer multicore atau multiprosesor jika beban kerja dibagi di antara banyak utas dalam strategi "membagi-dan-taklukkan" (lihat Bagian 5).

Eksekusi spekulatif

Pada mesin multicore, Anda kadang-kadang dapat meningkatkan kinerja dengan memprediksi sesuatu yang mungkin perlu dilakukan, dan kemudian melakukannya lebih dulu. LINQPad menggunakan teknik ini untuk mempercepat pembuatan kueri baru. Variasi adalah menjalankan sejumlah algoritma berbeda secara paralel yang semuanya menyelesaikan tugas yang sama. Yang mana saja yang menyelesaikan "kemenangan" pertama — ini efektif ketika Anda tidak dapat mengetahui sebelumnya algoritma mana yang akan mengeksekusi paling cepat.

Mengizinkan permintaan diproses secara bersamaan

Di server, permintaan klien dapat tiba secara bersamaan dan karenanya harus ditangani secara paralel (.NET Framework membuat utas untuk ini secara otomatis jika Anda menggunakan ASP.NET, WCF, Layanan Web, atau Remoting). Ini juga dapat berguna pada klien (misalnya, menangani jaringan peer-to-peer — atau bahkan beberapa permintaan dari pengguna).

Jika Anda tidak mencoba melakukan salah satu di atas, sebaiknya Anda berpikir keras tentang hal itu.

Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?

Jika Anda menggunakan .NET dan Anda telah menggunakan kasus penggunaan, Anda dapat menggunakan CHESS yang dapat menciptakan kembali kondisi interleaving utas yang memungkinkan Anda untuk menguji perbaikan Anda.

Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Itu tergantung pada konteksnya. Untuk skenario pekerja saya memikirkan bawahan manajer. Manger memberi tahu bawahan untuk melakukan sesuatu dan menunggu pembaruan status.

Untuk tugas yang tidak berhubungan secara bersamaan, saya memikirkan lift atau mobil di jalur lalu lintas yang terpisah.

Untuk sinkronisasi saya kadang-kadang memikirkan lampu lalu lintas atau gaya belok.

Juga jika Anda menggunakan C # 4.0 Anda mungkin ingin melihat di Perpustakaan Tugas Paralel

Conrad Frix
sumber
0

Jawaban saya untuk pertanyaan ini adalah:

  • Bagaimana Anda mengetahui apa yang bisa dibuat bersamaan vs apa yang harus dilakukan secara berurutan?

Pertama saya perlu tahu mengapa saya harus menggunakan konkurensi, karena saya telah mengetahui bahwa orang keluar dengan ide di balik konkurensi tetapi tidak selalu berpikir tentang masalah yang mereka coba selesaikan.

Jika Anda harus mensimulasikan situasi kehidupan nyata seperti antrian, alur kerja, dll, Anda kemungkinan besar akan perlu menggunakan pendekatan bersamaan.

Sekarang saya tahu bahwa saya harus menggunakannya, saatnya untuk menganalisis trade off, jika Anda memiliki banyak proses, Anda mungkin berpikir tentang overhead komunikasi, tetapi jika Anda harus baru, mungkin berakhir tanpa solusi bersamaan (reanalize problem if begitu.)

  • Bagaimana Anda mereproduksi kondisi kesalahan dan melihat apa yang terjadi ketika aplikasi dijalankan?

Saya bukan ahli dalam hal ini tetapi saya berpikir bahwa untuk sistem bersamaan ini bukan pendekatan yang benar. Pendekatan teoritis harus dipilih, mencari 4 persyaratan kebuntuan pada bidang kritis:

  1. Non preemptiveness
  2. Tahan dan tunggu
  3. Pengecualian secara motif
  4. Rantai melingkar

    • Bagaimana Anda memvisualisasikan interaksi antara berbagai bagian aplikasi yang berbeda?

Pertama-tama saya mencoba mengidentifikasi siapa yang menjadi peserta dalam interaksi, lalu bagaimana mereka berkomunikasi dan dengan siapa. Akhirnya, grafik dan diagram interaksi membantu saya memvisualisasikan. Papan tulis lama saya yang bagus tidak dapat dikalahkan oleh media jenis apa pun.

guiman
sumber
0

Saya akan berterus terang. Saya suka alat. Saya menggunakan banyak alat. Langkah pertama saya adalah menata jalur yang diinginkan untuk aliran negara. Langkah saya berikutnya adalah mencoba dan mencari tahu apakah itu layak, atau jika aliran informasi yang diperlukan akan membuat serial kode terlalu sering. Lalu, saya akan mencoba dan menyusun beberapa model sederhana. Ini dapat berkisar dari setumpuk patung tusuk gigi kasar hingga beberapa contoh sederhana serupa dengan python. Selanjutnya, saya melihat-lihat beberapa buku favorit saya, seperti buku kecil semafor, dan melihat apakah seseorang sudah menemukan solusi yang lebih baik untuk masalah saya.

Lalu saya mulai coding.
Hanya bercanda. Penelitian sedikit lebih dulu. Saya suka duduk dengan sesama peretas, dan berjalan melalui eksekusi yang diharapkan dari program di tingkat tinggi. Jika pertanyaan muncul, kami melangkah ke level yang lebih rendah. Penting untuk mengetahui apakah orang lain dapat memahami solusi Anda dengan cukup baik untuk memeliharanya.

Akhirnya, saya mulai coding. Saya mencoba untuk tetap sangat sederhana dulu. Hanya jalur kode, tidak ada yang mewah. Pindahkan status sesedikit mungkin. Hindari menulis. Hindari membaca yang dapat bertentangan dengan penulisan. Hindari, di atas segalanya, penulisan yang mungkin bertentangan dengan penulisan. Sangat mudah untuk menemukan bahwa Anda memiliki jumlah toksik yang positif, dan bahwa solusi indah Anda tiba-tiba sedikit lebih dari pendekatan serial cache-thrashing.

Aturan yang baik adalah menggunakan kerangka kerja di mana pun Anda bisa. Jika Anda menulis sendiri komponen threading dasar, seperti struktur data yang tersinkronisasi dengan baik, atau primitif sinkronisasi-sebenarnya yang dilarang, Anda hampir pasti akan meledakkan seluruh kaki Anda.

Akhirnya, alat. Debugging sangat sulit. Saya menggunakan valgrind \ callgrind di linux bersamaan dengan PIN, dan studio paralel di windows. Jangan mencoba dan men-debug hal ini dengan tangan. Anda mungkin bisa. Tetapi Anda mungkin berharap Anda tidak melakukannya. Sepuluh jam menguasai beberapa alat canggih, dan beberapa model bagus akan menghemat ratusan jam kemudian.

Di atas segalanya, bekerjalah secara bertahap. Bekerja dengan hati-hati. Jangan menulis kode bersamaan saat lelah. Jangan menulisnya saat lapar. Bahkan, jika Anda bisa menghindarinya, cukup jangan menuliskannya. Konkurensi sulit, dan saya telah menemukan bahwa banyak aplikasi yang mencantumkannya sebagai fitur yang sering disertakan sebagai satu-satunya fitur mereka.

Ringkasnya:
Mulailah:
Think
Talk
Test
Write, cukup
Baca
Test
Write
Debug
GOTO Begin

Jake Kurzer
sumber