Cara membuat metode dibatalkan tanpa itu menjadi jelek?
Saat ini saya dalam proses perkuatan kami berjalan lama metode yang akan dibatalkan. Saya berencana menggunakan Sistem.Threading.Tugas-tugas.CancellationToken untuk melaksanakan itu.
Metode kami biasanya melakukan beberapa lama berjalan beberapa langkah saja (mengirim perintah dan kemudian menunggu untuk hardware sebagian besar), misalnya
void Run()
{
Step1();
Step2();
Step3();
}
Pertama saya (mungkin bodoh) berpikir tentang pembatalan akan mengubah ini menjadi
bool Run(CancellationToken cancellationToken)
{
Step1(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
Step2(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
Step3(cancellationToken);
if (cancellationToken.IsCancellationRequested)
return false;
return true;
}
yang terus terang tampak mengerikan. Ini "pola" akan terus dalam langkah tunggal, terlalu (dan mereka tentu agak gondrong sudah). Hal ini akan membuat Thread.Abort() terlihat agak seksi, meskipun aku tahu tidak dianjurkan.
Apakah ada cleaner pola untuk mencapai hal ini yang tidak menyembunyikan aplikasi logika di bawah banyak kode boilerplate?
Edit
Sebagai contoh untuk sifat dari langkah-langkah, Run
metode bisa baca
void Run()
{
GiantRobotor.MoveToBase();
Oven.ThrowBaguetteTowardsBase();
GiantRobotor.CatchBaguette();
// ...
}
Kami mengendalikan perangkat keras yang berbeda dengan unit-unit yang membutuhkan untuk dapat disinkronisasi untuk bekerja bersama-sama.
Jika langkah-langkah yang entah bagaimana independend mengenai dataflow dalam metode, tetapi dapat't akan dieksekusi secara paralel masalah, pendekatan berikut mungkin lebih baik dibaca:
Jika langkah-langkah don't perlu pembatalan token karena anda dapat membaginya dalam unit kecil, anda bahkan dapat menulis lebih kecil daftar definisi:
bagaimana kelanjutan?
Saya akui itu isn't cukup, tetapi bimbingan yang lebih baik untuk melakukan apa yang telah anda lakukan:
...atau sedikit lebih pendek:
Umumnya, jika anda dapat lulus pembatalan token untuk langkah-langkah individu, anda dapat menyebarkan cek keluar seperti yang mereka don't jenuh kode. Anda mungkin juga memilih untuk tidak memeriksa untuk pembatalan constantly; jika operasi anda melakukan idempotent dan tidak't intensif sumber daya, anda don't perlu memeriksa untuk pembatalan pada setiap tahap. Waktu yang paling penting untuk memeriksa sebelum kembali hasilnya.
Jika anda're lewat token ke semua langkah anda, anda bisa melakukan sesuatu seperti ini:
Ketika saya harus melakukan sesuatu seperti itu, saya membuat sebuah delegasi untuk melakukan hal ini:
Atau, jika tidak pernah ada kode antara langkah-langkah, anda dapat menulis:
Versi pendek:
Penggunaan
kunci()
sinkronisasiBenang.Abort()
call dengan bagian penting.Mari saya jelaskan versi:
Umumnya, ketika membatalkan benang's'd harus mempertimbangkan dua jenis kode:
Selama menjalankan kode anda don't benar-benar peduli jika selesai
Jenis pertama adalah jenis yang kita don't peduli jika pengguna diminta batalkan. Mungkin kita menghitung sampai seratus miliar dan dia doesn't peduli lagi?
Jika kita'd gunakan sentinel seperti
CancellationToken
, kita tidak akan menguji mereka dalam setiap iterasi penting kode yang akan kita?Jadi jelek. Jadi untuk kasus ini,
Benang.Abort()
adalah anugerah.Sayangnya, karena beberapa orang mengatakan, anda bisa't menggunakan
Benang.Abort()
karena atom kode yang benar-benar memiliki untuk menjalankan! Kode telah dikurangi uang bentuk akun anda, sekarang anda harus menyelesaikan transaksi dan transfer uang ke akun target. Ada yang suka uang menghilang.Untungnya, kami telah saling pengecualian untuk membantu kita dengan hal semacam ini. Dan C# membuatnya cukup:
Dan di tempat lain
Jumlah kunci yang akan selalu menjadi
<=
jumlah penjaga, karena anda'd ingin sentinel pada kode penting juga (untuk membuatnya membatalkan lebih cepat). Hal ini membuat kode yang sedikit lebih cantik dari sentinel versi, tetapi juga membuat aborting baik, karena itu doesn't harus menunggu untuk hal yang tidak penting.Juga untuk dicatat adalah bahwa
ThreadAbortException
bisa timbul di mana saja, bahkan diakhirnya
blok. Ini ketidakpastian membuatBenang.Abort()
jadi kontroversial.Dengan kunci anda'd hindari hal ini dengan hanya mengunci seluruh
coba-menangkap-akhirnya
blok. Itu pembersihan diakhirnya
adalah penting, maka seluruh blok dapat dikunci.coba-coba
blok pada umumnya dibuat sesingkat mungkin, satu baris sebaiknya, jadi kita tidak't mengunci setiap kode yang tidak perlu.Hal ini membuat
Benang.Abort()
, seperti yang anda katakan, sedikit kurang jahat. Anda mungkin tidak ingin menyebutnya di UI benang, meskipun, seperti yang anda're sekarang menguncinya.Jika refactor diperbolehkan, anda bisa refactor Langkah metode seperti yang ada merupakan salah satu metode
Langkah(int nomor)
.Anda kemudian dapat loop melalui dari 1 sampai N dan memeriksa jika pembatalan token diminta hanya sekali.
Atau, ekuivalen: (Mana yang anda sukai)
Anda mungkin ingin mengambil melihat Apple's NSOperation pattern sebagai contoh. It's lebih rumit daripada hanya membuat satu metode dibatalkan, tapi itu's cukup kuat.
Saya agak heran, bahwa tidak ada yang telah mengusulkan standar, built-in, cara penanganan ini:
Sementara biasanya anda don't ingin mengandalkan mendorong cek ke tingkat yang lebih dalam, dalam hal ini langkah-langkah yang sudah menerima CancellationToken dan harus cek pula (sebagai harus setiap non-trivial metode menerima CancellationToken).
Hal ini juga memungkinkan anda untuk menjadi seperti butiran atau non-granular dengan pemeriksaan yang diperlukan, yaitu sebelum berjalan lama / operasi intensif.