Home
%3CLINGO-SUB%20id%3D%22lingo-sub-1340909%22%20slang%3D%22en-US%22%3EHow%20to%20use%20CancellationTokens%20to%20cancel%20operations%20in%20the%20Azure%20SDK%20for%20.NET%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1340909%22%20slang%3D%22en-US%22%3E%3CP%3EThe%20ability%20to%20cancel%20long-running%20operations%20is%20important%20to%20help%20keep%20applications%20responsive.%20Whether%20the%20network%20connection%20is%20slow%20or%20disconnects%2C%20or%20the%20user%20just%20wants%20to%20cancel%20a%20long%20operation%2C%20using%20a%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtoken%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationToken%3C%2FTT%3E%3C%2FA%3E%20in%20.NET%20makes%20it%20easy%20to%20cancel%20those%20long%20operations.%20Together%20with%20a%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtokensource%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationTokenSource%3C%2FTT%3E%3C%2FA%3E%2C%20a%20developer%20can%20provide%20on-demand%20or%20timed%20cancellations%20of%20operations%20that%20accept%20a%3CCODE%3ECancellationToken%3C%2FCODE%3E%2C%20like%20our%20%3CA%20href%3D%22https%3A%2F%2Fazure.github.io%2Fazure-sdk%2Fgeneral_design.html%23network-requests%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3Eclient%20methods%3C%2FA%3E%20in%20the%20Azure%20SDK%20for%20.NET.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--1355505340%22%20id%3D%22toc-hId--1355392695%22%20id%3D%22toc-hId--1355392695%22%3EUsing%20CancellationTokens%3C%2FH2%3E%0A%3CP%3EPrior%20to%20the%20introduction%20of%20the%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtoken%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationToken%3C%2FTT%3E%3C%2FA%3E%20structure%20in%20.NET%20Framework%204.0%2C%20it%20was%20common%20to%20use%20one%20or%20more%20%3CTT%3EWaitHandle%3C%2FTT%3E%20objects%20to%20synchronize%20threads.%20This%20same%20pattern%20has%20been%20used%20in%20native%20Windows%20applications%20for%20decades.%20When%20the%20asynchronous%20task%20pattern%20was%20introduced%20in%20.NET%2C%20a%20new%2C%20simpler%20pattern%20for%20cancelling%20tasks%20was%20also%20introduced.%20While%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20can%20still%20provide%20a%20%3CTT%3EWaitHandle%3C%2FTT%3E%20to%20synchronize%20threads%2C%20creating%20tokens%20and%20passing%20them%20to%20methods%20is%20much%20easier%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3ECancellationTokenSource%20cts%20%3D%20new%20CancellationTokenSource()%3B%0AKeyVaultSecret%20secret%20%3D%20await%20secretClient.GetSecretAsync(%22my-secret%22%2C%20cts.Token)%3B%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CTT%3ECancellationTokenSource.Token%3C%2FTT%3E%20returns%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20that%20can%20be%20passed%20to%20other%20methods%20further%20down%20the%20call%20stack%2C%20or%20even%20on%20other%20threads.%20When%20those%20tokens%20are%20canceled%2C%20any%20methods%20waiting%20on%20them%20should%20throw%20an%20%3CTT%3EOperationCanceledException%3C%2FTT%3E.%20Methods%20accepting%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20don't%20even%20have%20to%20be%20asynchronous.%20Our%20synchronous%20client%20methods%20in%20the%20Azure%20SDK%20for%20.NET%20also%20accept%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E.%20In%20both%20synchronous%20or%20asynchronous%20code%2C%20you%20can%20simply%20call%20%3CTT%3ECancellationToken.ThrowIfCancellationRequested()%3C%2FTT%3E%20to%20immediately%20throw%20if%20the%20token%20was%20in%20a%20canceled%20state.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Epublic%20void%20DoWork(CancellationToken%20cancellationToken%20%3D%20default)%0A%7B%0A%20%20%20%20cancellationToken.ThrowIfCancellationRequested()%3B%0A%0A%20%20%20%20%2F%2F%20Start%20long-running%20operation...%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3ENotice%20that%20we%20didn't%20have%20to%20check%20if%20%3CTT%3EcancellationToken%3C%2FTT%3E%20was%20null.%20As%20a%20value%20type%20in%20.NET%2C%20it%20cannot%20be%20null%20and%20even%20defined%20as%20its%20default%20value%2C%20%3CTT%3ECancellationToken.ThrowIfCancellationRequested()%3C%2FTT%3E%20will%20simply%20do%20nothing.%3C%2FP%3E%0A%3CP%3EIf%20you%20don't%20want%20to%20throw%20an%20exception%20but%20still%20want%20to%20check%20if%20a%20token%20was%20cancelled%2C%20you%20can%20check%20the%20%3CTT%3ECancellationToken.IsCancellationRequested%3C%2FTT%3E%20property.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId-1132007493%22%20id%3D%22toc-hId-1132120138%22%20id%3D%22toc-hId-1132120138%22%3ECancelling%20CancellationTokens%3C%2FH2%3E%0A%3CP%3EYou've%20seen%20a%20few%20ways%20to%20pass%20and%20use%20a%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtoken%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationToken%3C%2FTT%3E%3C%2FA%3E%2C%20but%20how%20do%20you%20actually%20cancel%20them%3F%20That%20ability%20is%20supported%20by%20the%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtokensource%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationTokenSource%3C%2FTT%3E%3C%2FA%3E.%20A%20%3CTT%3ECancellationTokenSource%3C%2FTT%3E%20can%20cancel%20tokens%20on%20demand%20or%20after%20a%20certain%20amount%20of%20time%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3ECancellationTokenSource%20cts%20%3D%20new%20CancellationTokenSource(TimeSpan.FromSeconds(30))%3B%0AConsole.CancelKeyPress%20%2B%3D%20(source%2C%20args)%20%3D%26gt%3B%0A%7B%0A%20%20%20%20Console.Error.WriteLine(%22Cancelling%20download...%22)%3B%0A%0A%20%20%20%20args.Cancel%20%3D%20true%3B%0A%20%20%20%20cts.Cancel()%3B%0A%7D%3B%0A%0AConsole.WriteLine(%22Downloading%20to%20%7B0%7D...%22%2C%20path)%3B%0A%0Atry%0A%7B%0A%20%20%20%20using%20(Stream%20fs%20%3D%20File.Create(path))%0A%20%20%20%20await%20blobClient.DownloadToAsync(fs%2C%20cts.Token)%3B%0A%7D%0Acatch%20(OperationCanceledException)%0A%7B%0A%20%20%20%20Console.Error.WriteLine(%22Download%20canceled.%20Deleting%20%7B0%7D...%22%2C%20path)%3B%0A%20%20%20%20File.Delete(path)%3B%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EWe%20created%20a%20%3CTT%3ECancellationTokenSource%3C%2FTT%3E%20that%20will%20cancel%20all%20its%20tokens%20after%2030%20seconds%2C%20and%20also%20hooked%20up%20a%20handler%20for%20pressing%20%3CSTRONG%3ECtrl%2BC%3C%2FSTRONG%3E%20in%20this%20sample%20console%20application.%20This%20way%2C%20we%20provide%20flexibility%20to%20the%20user%20to%20cancel%20the%20operation%20whenever%20they%20want%2C%20and%20also%20cancel%20the%20operation%20if%20it%20takes%20too%20long%2C%20which%20might%20indicate%20a%20network%20error%20if%20a%20suitable%20timeout%20is%20chosen.%20If%20the%20download%20is%20cancelled%2C%20we%20can%20handle%20the%20%3CTT%3EOperationCanceledException%3C%2FTT%3E%20to%20delete%20the%20file%20in%20case%20it%20was%20partially%20downloaded.%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--675446970%22%20id%3D%22toc-hId--675334325%22%20id%3D%22toc-hId--675334325%22%3EAdvanced%20uses%3C%2FH2%3E%0A%3CP%3EDespite%20its%20simple%20design%2C%20a%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtoken%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationToken%3C%2FTT%3E%3C%2FA%3E%20can%20be%20used%20in%20a%20number%20of%20other%20scenarios.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-15114504%22%20id%3D%22toc-hId-15227149%22%20id%3D%22toc-hId-15227149%22%3EDoing%20additional%20work%20when%20canceled%3C%2FH3%3E%0A%3CP%3ECanceling%20any%20current%20or%20pending%20work%20is%20a%20typical%20scenario%20when%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20is%20canceled.%26nbsp%3B%20In%20some%20scenarios%2C%20you%20may%20need%20to%20do%20some%20clean%20up%20only%20when%20canceled.%26nbsp%3B%20Register%20a%20delegate%20to%20run%20only%20when%20the%20%3CTT%3ECancellationToken%3C%2FTT%3E%20is%20canceled.%20Registering%20a%20delegate%20returns%20an%20%3CTT%3EIDisposable%3C%2FTT%3E%20which%20can%20be%20disposed%20to%20unregister%20the%20delegate%20if%20no%20longer%20needed.%20We%20could%20log%20a%20message%2C%20for%20example%2C%20that%20a%20method%20was%20canceled%20without%20worrying%20where%20in%20code%20to%20write%20the%20message%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Epublic%20async%20Task%20DoWorkAsync(CancellationToken%20cancellationToken%20%3D%20default)%0A%7B%0A%20%20%20%20using%20(CancellationTokenRegistration%20cts%20%3D%20cancellationToken.Register(()%20%3D%26gt%3B%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20Console.Error.WriteLine(%22The%20task%20was%20cancelled.%22)%3B%0A%20%20%20%20%7D))%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Start%20long-running%20task...%0A%20%20%20%20%7D%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId--1792339959%22%20id%3D%22toc-hId--1792227314%22%20id%3D%22toc-hId--1792227314%22%3ELinking%20CancellationTokens%3C%2FH3%3E%0A%3CP%3EThere%20may%20be%20times%20when%20you%20have%20a%20group%20or%20tasks%20you%20want%20to%20cancel%20individually%20or%20all%20together%3B%20for%20example%2C%20downloading%20as%20many%20files%20as%20possible%20and%20reporting%20on%20those%20that%20failed.%26nbsp%3B%20You%20can%20link%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20to%20a%20new%20%3CTT%3ECancellationTokenSource%3C%2FTT%3E%20so%20that%20when%20the%20original%20%3CTT%3ECancellationToken%3C%2FTT%3E%20is%20canceled%2C%20any%20tokens%20created%20from%20a%20linked%20%3CTT%3ECancellationTokenSource%3C%2FTT%3E%20are%20canceled%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Epublic%20async%20Task%20DownloadAsync(Uri%20uri%2C%20CancellationToken%20cancellationToken%20%3D%20default)%0A%7B%0A%20%20%20%20using%20(CancellationTokenSource%20cts%20%3D%20CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20cts.CancelAfter(TimeSpan.FromSeconds(30))%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Download%20file...%0A%20%20%20%20%7D%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3ENote%20that%20linked%20CancellationTokens%20only%20work%20in%20one%20direction.%26nbsp%3B%20Canceling%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20from%20a%20linked%20%3CTT%3ECancellationTokenSource%3C%2FTT%3E%20does%20not%20cancel%20the%20original%20%3CTT%3ECancellationToken%3C%2FTT%3E.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH3%20id%3D%22toc-hId-695172874%22%20id%3D%22toc-hId-695285519%22%20id%3D%22toc-hId-695285519%22%3EWaiting%20on%20handles%3C%2FH3%3E%0A%3CP%3EYou%20can%20use%20a%20%3CTT%3ECancellationToken%3C%2FTT%3E%20with%20code%20that%20requires%20waiting%20on%20one%20or%20more%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.waithandle%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3EWaitHandle%3C%2FTT%3E%3C%2FA%3E%20objects.%20This%20may%20be%20common%20for%20older%20applications%20or%20when%20interoperating%20with%20native%20code%20like%20with%20P%2FInvoke.%20The%20%3CTT%3ECancellationToken.WaitHandle%3C%2FTT%3E%20can%20be%20used%20in%20calls%20like%20%3CTT%3EWaitHandle.WaitAny%3C%2FTT%3E%3A%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3EAutoResetEvent%20evt%20%3D%20new%20AutoResetEvent(false)%3B%0AThreadPool.QueueUserWorkItem(state%20%3D%26gt%3B%20DoWork(state%2C%20evt))%3B%0A%0Aint%20which%20%3D%20WaitHandle.WaitAny(new%20WaitHandle%5B%5D%20%7B%20evt%2C%20cancellationToken.WaitHandle%7D)%3B%0Aif%20(which%20%3D%3D%201)%0A%7B%0A%20%20%20%20Console.Error.WriteLine(%22The%20operation%20was%20canceled.%22)%3B%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3EUpdating%20APIs%20for%20older%20applications%20to%20use%20newer%20classes%20like%20%3CTT%3ECancellationToken%3C%2FTT%3E%20may%20help%20when%20upgrading%20to%20newer%20components.%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId-684669770%22%20id%3D%22toc-hId-684782415%22%20id%3D%22toc-hId-684782415%22%3EFurther%20reading%3C%2FH2%3E%0A%3CUL%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtoken%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationToken%3C%2FTT%3E%3C%2FA%3E%3C%2FLI%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtokensource%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%3CTT%3ECancellationTokenSource%3C%2FTT%3E%3C%2FA%3E%3C%2FLI%3E%0A%3CLI%3E%3CA%20href%3D%22https%3A%2F%2Fazure.github.io%2Fazure-sdk%2Fgeneral_design.html%23network-requests%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3EAzure%20SDK%20guidelines%20for%20network%20requests%3C%2FA%3E.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH2%20id%3D%22toc-hId--1122784693%22%20id%3D%22toc-hId--1122672048%22%20id%3D%22toc-hId--1122672048%22%3EWant%20to%20hear%20more%3F%3C%2FH2%3E%0A%3CP%3EFollow%20us%20on%20Twitter%20at%20%3CA%20href%3D%22https%3A%2F%2Ftwitter.com%2FAzureSDK%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3E%40AzureSDK%3C%2FA%3E.%20We'll%20be%20covering%20more%20best%20practices%20in%20cloud-native%20development%20as%20well%20as%20providing%20updates%20on%20our%20progress%20in%20developing%20the%20next%20generation%20of%20Azure%20SDKs.%3C%2FP%3E%0A%3CP%3EContributors%20to%20this%20article%3A%20%3CEM%3E%3CA%20href%3D%22https%3A%2F%2Ftwitter.com%2Fmrhestew%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%3EHeath%20Stewart%3C%2FA%3E%3C%2FEM%3E.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-1340909%22%20slang%3D%22en-US%22%3E%3CP%3E%3CSPAN%3EThe%20ability%20to%20cancel%20long-running%20operations%20is%20important%20to%20help%20keep%20applications%20responsive.%20Whether%20the%20network%20connection%20is%20slow%20or%20disconnects%2C%20or%20the%20user%20just%20wants%20to%20cancel%20a%20long%20operation%2C%20using%20a%26nbsp%3B%3C%2FSPAN%3E%3CA%20class%3D%22rich-diff-level-one%22%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtoken%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%20target%3D%22_blank%22%3E%3CCODE%3ECancellationToken%3C%2FCODE%3E%3C%2FA%3E%3CSPAN%3E%26nbsp%3Bin%20.NET%20makes%20it%20easy%20to%20cancel%20those%20long%20operations.%20Together%20with%20a%26nbsp%3B%3C%2FSPAN%3E%3CA%20class%3D%22rich-diff-level-one%22%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fdotnet%2Fapi%2Fsystem.threading.cancellationtokensource%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%20target%3D%22_blank%22%3E%3CCODE%3ECancellationTokenSource%3C%2FCODE%3E%3C%2FA%3E%3CSPAN%3E%2C%20a%20developer%20can%20provide%20on-demand%20or%20timed%20cancellations%20of%20operations%20that%20accept%20a%26nbsp%3B%3C%2FSPAN%3E%3CCODE%20class%3D%22rich-diff-level-one%22%3ECancellationToken%3C%2FCODE%3E%3CSPAN%3E%2C%20like%20our%26nbsp%3B%3C%2FSPAN%3E%3CA%20class%3D%22rich-diff-level-one%22%20href%3D%22https%3A%2F%2Fazure.github.io%2Fazure-sdk%2Fgeneral_design.html%23network-requests%22%20rel%3D%22nofollow%20noopener%20noreferrer%20noopener%20noreferrer%20noopener%20noreferrer%22%20target%3D%22_blank%22%3Eclient%20methods%3C%2FA%3E%3CSPAN%3E%26nbsp%3Bin%20the%20Azure%20SDK%20for%20.NET.%3C%2FSPAN%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20class%3D%22rich-diff-level-zero%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-TEASER%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1401783%22%20slang%3D%22en-US%22%3ERe%3A%20How%20to%20use%20CancellationTokens%20to%20cancel%20operations%20in%20the%20Azure%20SDK%20for%20.NET%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1401783%22%20slang%3D%22en-US%22%3E%3CP%3EThanks%20a%20lot%2C%20Adrian%2C%20for%20the%20useful%20info!%26nbsp%3B%20I'd%20like%20to%20see%20'continue%20will%20be'%20according%20usage%20this%20methods%20for%20call%20Key%20Vault%20Services%20with%26nbsp%3B%20Timeout%20Policy%20with%20Optimistic%20and%20Pessimistic%20Strategy.%20Thanks%20in%20advance.%20We'll%20wait%20for%20the%20next%20posts.%3C%2FP%3E%3C%2FLINGO-BODY%3E
Microsoft

The ability to cancel long-running operations is important to help keep applications responsive. Whether the network connection is slow or disconnects, or the user just wants to cancel a long operation, using a CancellationToken in .NET makes it easy to cancel those long operations. Together with a CancellationTokenSource, a developer can provide on-demand or timed cancellations of operations that accept a CancellationToken, like our client methods in the Azure SDK for .NET.

 

Using CancellationTokens

Prior to the introduction of the CancellationToken structure in .NET Framework 4.0, it was common to use one or more WaitHandle objects to synchronize threads. This same pattern has been used in native Windows applications for decades. When the asynchronous task pattern was introduced in .NET, a new, simpler pattern for cancelling tasks was also introduced. While a CancellationToken can still provide a WaitHandle to synchronize threads, creating tokens and passing them to methods is much easier:

 

 

CancellationTokenSource cts = new CancellationTokenSource();
KeyVaultSecret secret = await secretClient.GetSecretAsync("my-secret", cts.Token);

 

 

CancellationTokenSource.Token returns a CancellationToken that can be passed to other methods further down the call stack, or even on other threads. When those tokens are canceled, any methods waiting on them should throw an OperationCanceledException. Methods accepting a CancellationToken don't even have to be asynchronous. Our synchronous client methods in the Azure SDK for .NET also accept a CancellationToken. In both synchronous or asynchronous code, you can simply call CancellationToken.ThrowIfCancellationRequested() to immediately throw if the token was in a canceled state.

 

 

public void DoWork(CancellationToken cancellationToken = default)
{
    cancellationToken.ThrowIfCancellationRequested();

    // Start long-running operation...
}

 

 

Notice that we didn't have to check if cancellationToken was null. As a value type in .NET, it cannot be null and even defined as its default value, CancellationToken.ThrowIfCancellationRequested() will simply do nothing.

If you don't want to throw an exception but still want to check if a token was cancelled, you can check the CancellationToken.IsCancellationRequested property.

 

Cancelling CancellationTokens

You've seen a few ways to pass and use a CancellationToken, but how do you actually cancel them? That ability is supported by the CancellationTokenSource. A CancellationTokenSource can cancel tokens on demand or after a certain amount of time:

 

 

CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
Console.CancelKeyPress += (source, args) =>
{
    Console.Error.WriteLine("Cancelling download...");

    args.Cancel = true;
    cts.Cancel();
};

Console.WriteLine("Downloading to {0}...", path);

try
{
    using (Stream fs = File.Create(path))
    await blobClient.DownloadToAsync(fs, cts.Token);
}
catch (OperationCanceledException)
{
    Console.Error.WriteLine("Download canceled. Deleting {0}...", path);
    File.Delete(path);
}

 

 

We created a CancellationTokenSource that will cancel all its tokens after 30 seconds, and also hooked up a handler for pressing Ctrl+C in this sample console application. This way, we provide flexibility to the user to cancel the operation whenever they want, and also cancel the operation if it takes too long, which might indicate a network error if a suitable timeout is chosen. If the download is cancelled, we can handle the OperationCanceledException to delete the file in case it was partially downloaded.

Advanced uses

Despite its simple design, a CancellationToken can be used in a number of other scenarios.

 

Doing additional work when canceled

Canceling any current or pending work is a typical scenario when a CancellationToken is canceled.  In some scenarios, you may need to do some clean up only when canceled.  Register a delegate to run only when the CancellationToken is canceled. Registering a delegate returns an IDisposable which can be disposed to unregister the delegate if no longer needed. We could log a message, for example, that a method was canceled without worrying where in code to write the message:

 

 

public async Task DoWorkAsync(CancellationToken cancellationToken = default)
{
    using (CancellationTokenRegistration cts = cancellationToken.Register(() =>
    {
        Console.Error.WriteLine("The task was cancelled.");
    }))
    {
        // Start long-running task...
    }
}

 

 

 

Linking CancellationTokens

There may be times when you have a group or tasks you want to cancel individually or all together; for example, downloading as many files as possible and reporting on those that failed.  You can link a CancellationToken to a new CancellationTokenSource so that when the original CancellationToken is canceled, any tokens created from a linked CancellationTokenSource are canceled:

 

 

public async Task DownloadAsync(Uri uri, CancellationToken cancellationToken = default)
{
    using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
    {
        cts.CancelAfter(TimeSpan.FromSeconds(30));

        // Download file...
    }
}

 

 

Note that linked CancellationTokens only work in one direction.  Canceling a CancellationToken from a linked CancellationTokenSource does not cancel the original CancellationToken.

 

Waiting on handles

You can use a CancellationToken with code that requires waiting on one or more WaitHandle objects. This may be common for older applications or when interoperating with native code like with P/Invoke. The CancellationToken.WaitHandle can be used in calls like WaitHandle.WaitAny:

 

 

AutoResetEvent evt = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem(state => DoWork(state, evt));

int which = WaitHandle.WaitAny(new WaitHandle[] { evt, cancellationToken.WaitHandle});
if (which == 1)
{
    Console.Error.WriteLine("The operation was canceled.");
}

 

 

Updating APIs for older applications to use newer classes like CancellationToken may help when upgrading to newer components.

Further reading

 

Want to hear more?

Follow us on Twitter at @AzureSDK. We'll be covering more best practices in cloud-native development as well as providing updates on our progress in developing the next generation of Azure SDKs.

Contributors to this article: Heath Stewart.

1 Comment
Regular Visitor

Thanks a lot, Adrian, for the useful info!  I'd like to see 'continue will be' according usage this methods for call Key Vault Services with  Timeout Policy with Optimistic and Pessimistic Strategy. Thanks in advance. We'll wait for the next posts.