Panduan Pemrogram .NET untuk CancellationToken

Diterbitkan: 2022-08-23

Terkadang membatalkan adalah hal yang baik. Dalam banyak proyek .NET saya, saya memiliki banyak motivasi untuk membatalkan proses internal dan eksternal. Microsoft mengetahui bahwa pengembang mendekati kasus penggunaan umum ini dalam berbagai implementasi kompleks dan memutuskan harus ada cara yang lebih baik. Dengan demikian, pola komunikasi pembatalan yang umum diperkenalkan sebagai CancellationToken , yang dibangun menggunakan konstruksi multithreading tingkat rendah dan komunikasi antarproses. Sebagai bagian dari penelitian awal saya tentang pola ini—dan setelah menggali kode sumber .NET aktual untuk implementasi Microsoft—saya menemukan bahwa CancellationToken dapat memecahkan serangkaian masalah yang jauh lebih luas: langganan pada status menjalankan aplikasi, waktu keluar operasi menggunakan berbagai pemicu, dan komunikasi antarproses umum melalui flag.

Kasus Penggunaan Token Pembatalan yang Dimaksudkan

CancellationToken diperkenalkan di .NET 4 sebagai sarana untuk meningkatkan dan menstandarisasi solusi yang ada untuk membatalkan operasi. Ada empat pendekatan umum untuk menangani pembatalan yang cenderung diterapkan oleh bahasa pemrograman populer:

Membunuh Katakan, jangan menerima jawaban tidak Mintalah dengan sopan, dan terima penolakan Atur bendera dengan sopan, biarkan polling jika mau
Mendekati Berhenti keras; menyelesaikan inkonsistensi nanti Katakan padanya untuk berhenti tetapi biarkan dia membersihkan semuanya Permintaan langsung tapi lembut untuk berhenti Minta berhenti, tapi jangan dipaksa
Ringkasan Jalan pasti menuju korupsi dan rasa sakit Memungkinkan titik berhenti yang bersih tetapi harus berhenti Memungkinkan titik pemberhentian yang bersih, tetapi permintaan pembatalan dapat diabaikan Pembatalan diminta melalui bendera
Pthreads pthread_kill ,
pthread_cancel (async)
pthread_cancel (mode ditangguhkan) tidak ada Melalui sebuah bendera
.BERSIH Thread.Abort tidak ada Thread.Interrupt Melalui bendera di CancellationToken
Jawa Thread.destroy ,
Thread.stop
tidak ada Thread.interrupt Melalui bendera atau Thread.interrupted
Python PyThreadState_SetAsyncExc tidak ada asyncio.Task.cancel Melalui sebuah bendera
Panduan tidak dapat diterima; hindari pendekatan ini Dapat diterima, terutama ketika bahasa tidak mendukung pengecualian atau pelepasan Dapat diterima jika bahasa mendukungnya Lebih baik, tetapi lebih merupakan upaya kelompok
Ringkasan Pendekatan Pembatalan dan Contoh Bahasa

CancellationToken berada di kategori terakhir, di mana percakapan pembatalan bersifat kooperatif.

Setelah Microsoft memperkenalkan CancellationToken , komunitas pengembangan dengan cepat menerimanya, terutama karena banyak .NET API utama diperbarui untuk menggunakan token ini secara asli. Misalnya, dimulai dengan ASP.NET Core 2.0, tindakan mendukung parameter CancellationToken opsional yang mungkin memberi sinyal jika permintaan HTTP telah ditutup, memungkinkan pembatalan operasi apa pun dan dengan demikian menghindari penggunaan sumber daya yang tidak perlu.

Setelah menyelam jauh ke dalam basis kode .NET, menjadi jelas bahwa penggunaan CancellationToken tidak terbatas pada pembatalan.

PembatalanToken Di Bawah Mikroskop

Saat melihat lebih dekat pada implementasi CancellationToken , kami melihat itu hanya sebuah flag sederhana (yaitu, ManualResetEvent ) dan infrastruktur pendukung yang menyediakan kemampuan untuk memantau dan mengubah flag tersebut. Utilitas utama CancellationToken ada dalam namanya, yang menunjukkan ini adalah cara umum untuk membatalkan operasi. Saat ini, perpustakaan, paket, atau kerangka kerja .NET apa pun dengan operasi asinkron atau yang berjalan lama memungkinkan pembatalan melalui token ini.

CancellationToken dapat dipicu baik dengan menyetel flag-nya secara manual ke "true" atau memprogramnya untuk berubah menjadi "true" setelah rentang waktu tertentu berlalu. Terlepas dari bagaimana CancellationToken dipicu, kode klien yang memantau token ini dapat menentukan nilai tanda tanda melalui salah satu dari tiga metode:

  • Menggunakan WaitHandle
  • Polling bendera CancellationToken
  • Memberi tahu kode klien saat status bendera diperbarui melalui langganan terprogram

Setelah penelitian lebih lanjut dalam basis kode .NET, menjadi jelas bahwa tim .NET menemukan CancellationTokens berguna dalam skenario lain yang tidak terkait dengan pembatalan. Mari kita jelajahi beberapa kasus penggunaan tingkat lanjut dan di luar merek ini, yang memberdayakan pengembang C# dengan koordinasi multithread dan antarproses untuk menyederhanakan situasi yang kompleks.

Token Pembatalan untuk Acara Lanjutan

Saat menulis aplikasi ASP.NET Core, terkadang kita perlu mengetahui kapan aplikasi kita telah dimulai, atau kita perlu memasukkan kode kita ke dalam proses shutdown host. Dalam kasus tersebut, kami menggunakan antarmuka IHostApplicationLifetime (sebelumnya IApplicationLifetime ). Antarmuka ini (dari repositori .NET Core) menggunakan CancellationToken untuk mengomunikasikan tiga peristiwa utama: ApplicationStarted , ApplicationStopping , dan ApplicationStopped :

 namespace Microsoft.Extensions.Hosting { /// <summary> /// Allows consumers to be notified of application lifetime events. /// This interface is not intended to be user-replaceable. /// </summary> public interface IHostApplicationLifetime { /// <summary> /// Triggered when the application host has fully started. /// </summary> CancellationToken ApplicationStarted { get; } /// <summary> /// Triggered when the application host is starting a graceful shutdown. /// Shutdown will block until all callbacks registered on /// this token have completed. /// </summary> CancellationToken ApplicationStopping { get; } /// <summary> /// Triggered when the application host has completed a graceful shutdown. /// The application will not exit until all callbacks registered on /// this token have completed. /// </summary> CancellationToken ApplicationStopped { get; } /// <summary> /// Requests termination of the current application. /// </summary> void StopApplication(); } }

Sepintas, sepertinya CancellationToken s tidak pantas di sini, terutama karena mereka digunakan sebagai event. Namun, pemeriksaan lebih lanjut mengungkapkan token ini sangat cocok:

  • Mereka fleksibel, memungkinkan berbagai cara bagi klien antarmuka untuk mendengarkan acara ini.
  • Mereka aman dari kotak.
  • Mereka dapat dibuat dari sumber yang berbeda dengan menggabungkan CancellationToken s.

Meskipun CancellationToken s tidak sempurna untuk setiap kebutuhan acara, mereka ideal untuk acara yang hanya terjadi sekali, seperti aplikasi mulai atau berhenti.

CancellationToken untuk Timeout

Secara default, ASP.NET memberi kita sedikit waktu untuk mematikan. Dalam kasus di mana kita menginginkan sedikit lebih banyak waktu, menggunakan kelas HostOptions memungkinkan kita untuk mengubah nilai batas waktu ini. Di bawahnya, nilai batas waktu ini dibungkus dalam CancellationToken dan dimasukkan ke dalam subproses yang mendasarinya.

Metode IHostedService StopAsync adalah contoh yang bagus dari penggunaan ini:

 namespace Microsoft.Extensions.Hosting { /// <summary> /// Defines methods for objects that are managed by the host. /// </summary> public interface IHostedService { /// <summary> /// Triggered when the application host is ready to start the service. /// </summary> /// <param name="cancellationToken">Indicates that the start /// process has been aborted.</param> Task StartAsync(CancellationToken cancellationToken); /// <summary> /// Triggered when the application host is performing a graceful shutdown. /// </summary> /// <param name="cancellationToken">Indicates that the shutdown /// process should no longer be graceful.</param> Task StopAsync(CancellationToken cancellationToken); } }

Seperti yang terlihat dalam definisi antarmuka IHostedService , metode StopAsync mengambil satu parameter CancellationToken . Komentar yang terkait dengan parameter tersebut dengan jelas mengomunikasikan maksud awal Microsoft untuk CancellationToken adalah sebagai mekanisme batas waktu, bukan sebagai proses pembatalan.

Menurut pendapat saya, jika antarmuka ini sudah ada sebelum keberadaan CancellationToken , ini bisa menjadi parameter TimeSpan —untuk menunjukkan berapa lama operasi penghentian diizinkan untuk diproses. Dalam pengalaman saya, skenario batas waktu hampir selalu dapat dikonversi ke CancellationToken dengan utilitas tambahan yang hebat.

Untuk saat ini, mari kita lupakan bahwa kita tahu bagaimana metode StopAsync dirancang dan alih-alih berpikir tentang bagaimana kita akan merancang kontrak metode ini. Pertama mari kita tentukan persyaratannya:

  • Metode StopAsync harus mencoba menghentikan layanan.
  • Metode StopAsync harus memiliki status berhenti yang anggun.
  • Terlepas dari apakah status berhenti anggun tercapai, layanan yang dihosting harus memiliki waktu maksimum untuk berhenti, seperti yang ditentukan oleh parameter batas waktu kami.

Dengan memiliki metode StopAsync dalam bentuk apa pun, kami memenuhi persyaratan pertama. Persyaratan yang tersisa rumit. CancellationToken memenuhi persyaratan ini secara tepat dengan menggunakan alat komunikasi berbasis flag .NET standar untuk memberdayakan percakapan.

CancellationToken Sebagai Mekanisme Notifikasi

Rahasia terbesar di balik CancellationToken adalah bahwa itu hanya sebuah bendera. Mari kita ilustrasikan bagaimana CancellationToken dapat digunakan untuk memulai proses alih-alih menghentikannya.

Pertimbangkan hal berikut:

  1. Buat kelas RandomWorker .
  2. RandomWorker harus memiliki metode DoWorkAsync yang menjalankan beberapa pekerjaan acak.
  3. Metode DoWorkAsync harus mengizinkan pemanggil untuk menentukan kapan pekerjaan harus dimulai.
 public class RandomWorker { public RandomWorker(int id) { Id = id; } public int Id { get; } public async Task DoWorkAsync() { for (int i = 1; i <= 10; i++) { Console.WriteLine($"[Worker {Id}] Iteration {i}"); await Task.Delay(1000); } } }

Kelas di atas memenuhi dua persyaratan pertama, meninggalkan kami dengan yang ketiga. Ada beberapa antarmuka alternatif yang dapat kita gunakan untuk memicu pekerja kita, seperti rentang waktu atau tanda sederhana:

 # With a time span Task DoWorkAsync(TimeSpan startAfter); # Or a simple flag bool ShouldStart { get; set; } Task DoWorkAsync();

Kedua pendekatan ini baik-baik saja, tetapi tidak ada yang seanggun menggunakan CancellationToken :

 public class RandomWorker { public RandomWorker(int id) { Id = id; } public int Id { get; } public async Task DoWorkAsync(CancellationToken startToken) { startToken.WaitHandle.WaitOne(); for (int i = 1; i <= 10; i++) { Console.WriteLine($"[Worker {Id}] Iteration {i}"); await Task.Delay(1000); } } }

Contoh kode klien ini menggambarkan kekuatan desain ini:

 using System; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace CancelToStart { public class Program { static void Main(string[] args) { CancellationTokenSource startCts = new CancellationTokenSource(); startCts.CancelAfter(TimeSpan.FromSeconds(10)); var tasks = Enumerable.Range(0, 10) .Select(i => new RandomWorker(i)) .Select(worker => worker.DoWorkAsync(startCts.Token)) .ToArray(); Task.WaitAll(tasks, CancellationToken.None); } } }

CancellationTokenSource akan membuat CancellationToken kami di belakang layar dan mengoordinasikan pemicuan semua proses terkait. Dalam hal ini, proses terkait adalah RandomWorker kami, yang menunggu untuk dimulai. Pendekatan ini memungkinkan kami untuk memanfaatkan keamanan utas yang dimasukkan ke dalam implementasi CancellationToken default.

Kotak Alat Token Pembatalan yang Luas

Contoh-contoh ini menunjukkan bagaimana CancellationToken menyediakan kotak alat solusi yang berguna di luar kasus penggunaan yang dimaksudkan. Alat ini dapat berguna dalam banyak skenario yang melibatkan komunikasi berbasis flag antarproses. Apakah kita dihadapkan dengan batas waktu, pemberitahuan, atau peristiwa satu kali, kita dapat kembali pada implementasi elegan yang telah diuji oleh Microsoft ini.

Dari atas ke bawah, kata "Emas" (berwarna emas), "Microsoft," dan "Mitra" (keduanya berwarna hitam) muncul diikuti dengan logo Microsoft.
Sebagai Mitra Emas Microsoft, Toptal adalah jaringan elit pakar Microsoft Anda. Bangun tim berkinerja tinggi dengan para ahli yang Anda butuhkan—di mana pun dan kapan pun Anda membutuhkannya!

Bacaan Lebih Lanjut di Blog Teknik Toptal:

  • .NET Core: Menjadi Liar dan Sumber Terbuka. Microsoft, apa yang membuatmu begitu lama?!
  • Membangun ASP.NET Web API Dengan ASP.NET Core
  • Cara Boot-strap dan Membuat Proyek .NET