Kelas CancellationTokenSource
ini sekali pakai. Pandangan cepat di Reflector membuktikan penggunaan KernelEvent
, sumber daya yang (tidak mungkin) dikelola. Karena CancellationTokenSource
tidak memiliki finalizer, jika kita tidak membuangnya, GC tidak akan melakukannya.
Di sisi lain, jika Anda melihat sampel yang terdaftar di artikel MSDN Pembatalan di Thread yang Dikelola , hanya satu cuplikan kode yang membuang token.
Apa cara yang tepat untuk membuangnya dalam kode?
- Anda tidak dapat membungkus kode dengan memulai tugas paralel
using
Anda jika Anda tidak menunggu. Dan masuk akal untuk memiliki pembatalan hanya jika Anda tidak menunggu. - Tentu saja Anda dapat menambahkan
ContinueWith
tugas denganDispose
panggilan, tetapi apakah itu cara untuk pergi? - Bagaimana dengan permintaan PLINQ yang dapat dibatalkan, yang tidak disinkronkan kembali, tetapi hanya melakukan sesuatu pada akhirnya? Katakan saja
.ForAll(x => Console.Write(x))
? - Apakah bisa digunakan kembali? Dapatkah token yang sama digunakan untuk beberapa panggilan dan kemudian membuangnya bersama-sama dengan komponen host, misalkan kontrol UI?
Karena tidak memiliki sesuatu seperti Reset
metode untuk pembersihan IsCancelRequested
dan Token
bidang saya kira itu tidak dapat digunakan kembali, sehingga setiap kali Anda memulai tugas (atau permintaan PLINQ), Anda harus membuat yang baru. Apakah itu benar Jika ya, pertanyaan saya adalah apa strategi yang tepat dan direkomendasikan untuk menangani Dispose
banyak CancellationTokenSource
contoh?
sumber
Important: The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.
- docs.microsoft.com/en-us/dotnet/standard/threading/…Saya tidak berpikir ada jawaban yang memuaskan. Setelah meneliti saya menemukan jawaban ini dari Stephen Toub ( referensi ):
Bagian yang berani menurut saya adalah bagian yang penting. Dia menggunakan "lebih berdampak" yang membuatnya agak kabur. Saya menafsirkannya sebagai pemanggilan yang berarti
Dispose
dalam situasi-situasi itu harus dilakukan, jika tidak menggunakanDispose
tidak diperlukan.sumber
Saya melihat di ILSpy untuk
CancellationTokenSource
tetapi saya hanya bisa menemukanm_KernelEvent
yang sebenarnyaManualResetEvent
, yang merupakan kelas pembungkus untukWaitHandle
objek. Ini harus ditangani dengan benar oleh GC.sumber
Anda harus selalu membuang
CancellationTokenSource
.Cara membuangnya tergantung pada skenario. Anda mengusulkan beberapa skenario berbeda.
using
hanya berfungsi ketika Anda menggunakanCancellationTokenSource
pada beberapa pekerjaan paralel yang Anda tunggu. Jika itu senario Anda, maka hebat, itu metode termudah.Saat menggunakan tugas, gunakan
ContinueWith
tugas yang Anda inginkanCancellationTokenSource
.Untuk plinq Anda dapat menggunakannya
using
karena Anda menjalankannya secara paralel tetapi menunggu semua pekerja yang berjalan paralel selesai.Untuk UI, Anda dapat membuat yang baru
CancellationTokenSource
untuk setiap operasi yang dapat dibatalkan yang tidak terkait dengan satu pemicu batal tunggal. PertahankanList<IDisposable>
dan tambahkan setiap sumber ke daftar, buang semuanya saat komponen Anda dibuang.Untuk utas, buat utas baru yang bergabung dengan semua utas pekerja dan tutup satu sumber saat semua utas pekerja selesai. Lihat Pembatalan Sumber Daya, Kapan membuang?
Selalu ada jalan.
IDisposable
contoh harus selalu dibuang. Sampel sering tidak karena mereka baik sampel cepat untuk menunjukkan penggunaan inti atau karena menambahkan dalam semua aspek kelas yang diperlihatkan akan terlalu rumit untuk sampel. Sampel hanyalah sampel, tidak harus (atau bahkan biasanya) kode kualitas produksi. Tidak semua sampel dapat diterima untuk disalin ke dalam kode produksi apa adanya.sumber
await
pada tugas dan membuang CancertokenSource dalam kode yang datang setelah menunggu?await
operasi, Anda dapat melanjutkan karenaOperationCanceledException
. Anda mungkin akan meneleponDispose()
. Tetapi jika ada operasi yang masih berjalan dan menggunakan yang sesuaiCancellationToken
, token itu masih dilaporkanCanBeCanceled
sebagaitrue
sumber meskipun dibuang. Jika mereka mencoba mendaftarkan callback pembatalan, BOOM! ,ObjectDisposedException
. Cukup aman untuk meneleponDispose()
setelah operasi berhasil diselesaikan. Ini menjadi sangat rumit ketika Anda benar-benar perlu membatalkan sesuatu.Jawaban ini masih muncul dalam pencarian Google, dan saya percaya jawaban yang dipilih tidak memberikan cerita lengkap. Setelah melihat kode sumber untuk
CancellationTokenSource
(CTS) danCancellationToken
(CT) saya percaya bahwa untuk sebagian besar kasus penggunaan urutan kode berikut baik-baik saja:Bidang
m_kernelHandle
internal yang disebutkan di atas adalah objek sinkronisasi yang mendukungWaitHandle
properti di kedua kelas CTS dan CT. Ini hanya dipakai jika Anda mengakses properti itu. Jadi, kecuali Anda menggunakanWaitHandle
sinkronisasi sekolah lama dalamTask
pemanggilan panggilan Anda tidak akan berpengaruh.Tentu saja, jika Anda sedang menggunakannya Anda harus melakukan apa yang disarankan oleh jawaban yang lain di atas dan keterlambatan panggilan
Dispose
sampai setiapWaitHandle
operasi menggunakan pegangan yang lengkap, karena, seperti yang dijelaskan dalam API Windows dokumentasi untuk WaitHandle , hasilnya tidak terdefinisi.sumber
IsCancellationRequested
properti token dengan polling, panggilan balik, atau gagang tunggu." Dengan kata lain: Ini mungkin bukan Anda (yaitu orang yang membuat permintaan async) yang menggunakan pegangan tunggu, mungkin pendengar (yaitu orang yang menjawab permintaan). Yang berarti Anda, sebagai orang yang bertanggung jawab untuk membuang, secara efektif tidak memiliki kendali atas apakah pegangan tunggu digunakan atau tidak.Sudah lama sejak saya menanyakan hal ini dan mendapatkan banyak jawaban yang bermanfaat tetapi saya menemukan masalah menarik terkait hal ini dan berpikir saya akan mempostingnya di sini sebagai jawaban lain:
Anda harus menelepon
CancellationTokenSource.Dispose()
hanya ketika Anda yakin tidak ada yang akan mencoba untuk mendapatkanToken
properti CTS . Kalau tidak, Anda tidak boleh menyebutnya, karena itu adalah ras. Misalnya, lihat di sini:https://github.com/aspnet/AspNetKatana/issues/108
Dalam perbaikan untuk masalah ini, kode yang sebelumnya dilakukan
cts.Cancel(); cts.Dispose();
diedit hanya dilakukancts.Cancel();
karena ada orang yang kurang beruntung untuk mencoba mendapatkan token pembatalan untuk mengamati status pembatalannya setelahDispose
dipanggil, sayangnya juga perlu ditanganiObjectDisposedException
- selainOperationCanceledException
yang mereka rencanakan.Pengamatan kunci lain yang terkait dengan perbaikan ini dibuat oleh Tratcher: "Pembuangan hanya diperlukan untuk token yang tidak akan dibatalkan, karena pembatalan melakukan semua pembersihan yang sama." yaitu hanya melakukan
Cancel()
alih - alih membuang benar-benar cukup baik!sumber
Saya membuat kelas thread-safe yang mengikat a
CancellationTokenSource
keTask
, dan menjamin bahwaCancellationTokenSource
akan dibuang ketika terkaitTask
selesai. Menggunakan kunci untuk memastikan bahwaCancellationTokenSource
tidak akan dibatalkan selama atau setelah dibuang. Ini terjadi untuk kepatuhan dengan dokumentasi , yang menyatakan:Dan juga :
Inilah kelasnya:
Metode utama
CancelableExecution
kelas adalahRunAsync
danCancel
. Secara default, operasi konkuren tidak diperbolehkan, artinya memanggilRunAsync
kedua kalinya secara diam-diam akan membatalkan dan menunggu penyelesaian operasi sebelumnya (jika masih berjalan), sebelum memulai operasi baru.Kelas ini dapat digunakan dalam aplikasi apa pun. Penggunaan utamanya adalah dalam aplikasi UI, di dalam formulir dengan tombol untuk memulai dan membatalkan operasi asinkron, atau dengan kotak daftar yang membatalkan dan memulai kembali operasi setiap kali item yang dipilih diubah. Ini adalah contoh dari kasus pertama:
The
RunAsync
Metode menerima tambahanCancellationToken
sebagai argumen, yang terkait dengan dibuat secara internalCancellationTokenSource
. Memasok token opsional ini mungkin berguna dalam skenario tingkat lanjut.sumber