Masalah dan konteks
Soalan ini menguji model objek teras dalam C++26 std::execution. Mulakan dengan satu operasi tak segerak (asynchronous) yang minimum dan terangkan perihalan lazy bagi sender, kontrak penyelesaian receiver, operation state yang dihasilkan oleh connect, dan titik di mana start melancarkan pengiraan.
Perkara yang dinilai oleh penemu duga
Perbezaan utama adalah antara perihalan sender yang lazy dan operation state yang dicipta selepas penyambungan: connect membina keadaan (state), manakala start memulakan kerja. Terangkan cara set_value, set_error, dan set_stopped bergerak melalui saluran paip (pipeline), sama ada pertukaran scheduler menjejaskan afiniti bebenang (thread affinity), dan cara pembatalan menghalang kesan sampingan baharu.
Soalan penjelasan untuk ditanya terlebih dahulu
Kerja dan tekanan balikan (backpressure)
Tanya tentang saiz kelompok (batch size), konkurensi yang dibenarkan, susunan input, dan dasar percubaan semula (retry policy) untuk penulisan yang gagal. Jawapannya menentukan sama ada perlu mengehadkan konkurensi sender atau membatasi giliran (queue).
Penjadualan dan sumber henti (stop sources)
Tanya bagaimana scheduler I/O dan CPU didedahkan, sama ada hentian berpunca daripada tamat masa (timeout) atau tindakan pengguna, dan bagaimana panggilan sistem yang telah diserahkan diselesaikan. Permintaan henti bukan penamatan paksa bebenang.
Sumber dan semantik komit (commit)
Jelaskan pemilikan pemegang fail (file handles), penimbal (buffers), dan fail sementara, sama ada penulisan adalah idempoten, dan sama ada kelompok separa boleh diundur semula (roll back). Ini mentakrifkan pembersihan selepas set_error.
Rangka jawapan 30 saat
"Saya menerangkan baca, urai (parse), dan tulis dengan penyesuai sender, kemudian menggunakan penyesuai scheduler untuk memindahkan konteks pelaksanaan. Saluran paip kekal lazy: connect mencipta operation state dan start memulakannya. Setiap peringkat menghantar nilai kejayaan ke hilir, ralat melalui seterror, dan pembatalan melalui setstopped; satu stop token sampai ke setiap peringkat. Laluan henti menyemak sebelum kesan sampingan berlaku, manakala pemilik menyelesaikan dan menutup I/O yang telah diserahkan serta membersihkan fail sementara."
Langkah penyelesaian terperinci
Langkah 1: Takrifkan sempadan nilai dan ralat
Takrifkan jenis input dan output untuk setiap peringkat dan ubah kegagalan yang boleh dipulihkan kepada sender ralat yang eksplisit. Jangan biarkan pengecualian (exceptions) merentasi sempadan scheduler; biarkan receiver akhir merekodkan kejayaan, kegagalan, atau hentian.
Langkah 2: Gubah saluran paip yang lazy
Gunakan let_value, then, atau penyesuai yang setara untuk menyambungkan pembacaan, penghuraian, dan penulisan. Penggubahan membina perihalan tanpa memperuntukkan bebenang atau melakukan I/O; operation state memiliki sebarang keadaan dikongsi yang mesti bertahan lebih lama daripada panggilan balik (callback).
Langkah 3: Sambung dan mula (connect and start)
Panggil connect(sender, receiver) untuk mendapatkan operation state, kekalkannya dalam skop aktif, kemudian panggil start. Receiver mesti hidup lebih lama daripada operation state; panggilan balik tak segerak tidak boleh merujuk objek tindanan (stack objects) yang telah dimusnahkan.
Langkah 4: Bergerak antara konteks pelaksanaan
Selepas I/O selesai, gunakan scheduler sender untuk memindahkan penghuraian ke kolam CPU, kemudian kembalikan penulisan ke kolam I/O yang terbatas. Rekodkan kapasiti giliran dan keadilan (fairness); jangan letakkan penulisan yang menyekat (blocking) ke dalam kolam umum tanpa batasan.
Langkah 5: Sebarkan hentian dan tekanan balikan
Hantar stop token ke setiap peringkat yang boleh diganggu. Selepas hentian, jangan masukkan kelompok baharu ke dalam giliran; batalkan atau selesaikan panggilan sistem yang sedang berjalan mengikut API-nya. Apabila giliran penuh, sender pendikit (throttling sender) menjeda kerja huluan untuk membatasi memori.
Langkah 6: Kendalikan ralat dan kesan sampingan separa
Tulis ke fail sementara atau rekodkan jujukan kelompok sebelum melakukan komit secara atomik. set_error mencetuskan pembersihan hiliran dan penutupan pemegang. Percubaan semula memerlukan had dan kunci keidempotanan (idempotency key) supaya ia tidak menduplikasi penulisan.
Langkah 7: Sahkan konkurensi dan jangka hayat (lifetime)
Uji pelbagai scheduler, perlumbaan henti (stop races), kegagalan huraian, penulisan pendek, dan pemusnahan receiver awal. Gunakan penganalisis bebenang untuk perlumbaan data dan ukur masa giliran, daya pemprosesan (throughput), kependaman henti, dan operation state yang tidak dilepaskan.
Contoh jawapan berkualiti tinggi
Sender baca memancarkan kelompok, sender urai berjalan pada scheduler CPU, dan sender tulis berjalan pada scheduler I/O yang terbatas. Saluran paip hanya menerangkan kebergantungan; connect mencipta operation state dan start melancarkannya. Setiap peringkat mengendalikan penyelesaian value, error, dan stopped serta berkongsi satu stop token. Pemberhentian menyekat kelompok baharu, membolehkan I/O yang telah diserahkan ditutup dengan selamat, dan menggunakan fail sementara serta ID kelompok untuk percubaan semula yang idempoten. Ujian merangkumi pertukaran scheduler, tekanan balikan, perlumbaan henti, dan jangka hayat receiver.
Kesilapan lazim
- Kesilapan: Menganggap sender yang telah dibina sudah berjalan. → Sebab: Sender adalah bersifat lazy. → Pembetulan: Nyatakan sempadan connect/start.
- Kesilapan: Mengendalikan pengecualian tetapi tidak mengendalikan penyelesaian stopped. → Sebab: Henti ialah saluran penyelesaian yang berasingan. → Pembetulan: Laksanakan kedua-dua seterror dan setstopped.
- Kesilapan: Mematikan bebenang semasa pembatalan. → Sebab: Ia mungkin memiliki pemegang fail atau penulisan separa. → Pembetulan: Sebarkan stop token dan lakukan unwinding dengan selamat.
- Kesilapan: Memasukkan kelompok ke dalam giliran tanpa batasan. → Sebab: Ketiadaan tekanan balikan boleh menghabiskan memori. → Pembetulan: Bataskan konkurensi, kapasiti giliran, dan percubaan semula.
Soalan dan jawapan susulan
Soalan susulan 1: Mengapa tidak menggunakan future secara terus?
Sender/receiver menjadikan penjadualan, pembatalan, dan tiga saluran penyelesaian boleh digubah serta memberi kawalan jangka hayat semasa penyambungan. Future biasanya memerlukan konvensyen tambahan untuk penyebaran henti dan ralat.
Soalan susulan 2: Bolehkah sender dimusnahkan selepas start?
Perihalan sender sementara boleh dihapuskan, tetapi operation state, receiver, dan sumber yang ditangkap mesti kekal sah sehingga selesai. Objek tugas atau skop harus memilikinya.
Soalan susulan 3: Adakah set_stopped mengundur semula setiap kesan sampingan?
Tidak. Ia melaporkan penyelesaian yang dihentikan; I/O yang telah diserahkan mungkin tidak diundur semula. Fail sementara, komit yang idempoten, atau pampasan diperlukan untuk ketekalan.
Soalan susulan 4: Bagaimanakah anda membuktikan penulisan tidak diduplikasi?
Tetapkan ID kelompok yang stabil, semak ID yang telah dikomit sebelum menulis, dan biarkan percubaan semula hanya menulis ID yang hilang. Suntik kegagalan, mula semula, dan perlumbaan henti, kemudian bandingkan log komit dengan fail akhir.