-
Notifications
You must be signed in to change notification settings - Fork 4.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Asynchronously cancel a CancellationTokenSource #23405
Comments
I had a look at the CancellationTokenSource code and put together this hack. It uses a private field in CancellationTokenSource to figure out if any callbacks have ever been registered and decides based on that whether to invoke Cancel synchronously or via Task.Run. I'm offering it here as a possible workaround for those for whom whom this behaviour is badly needed. A warning though. I'm anything but an expert at async code. I'm inviting those who are to please check if it has any major disadvantages other than being vulnerable to internal changes in the CancellationTokenSource class. EDIT: The original version below gives no option to await the async invocation of the callbacks . This extension method version provides that ability.
EDIT2: Name the method to make it clear that it is async and return Task that can be awaited
|
It's needed feature. Especially when you use |
This issue was opened first, but I'm going to close it as a dup in favor of the broader #31315. Thanks! |
Actually, I'm going to reopen this one for CancelAsync and remove CancelAsync from the other one. |
Looks good as proposed namespace System.Threading
{
public partial class CancellationTokenSource
{
public Task CancelAsync();
}
} |
Proposal: Add a way to asynchronously cancel a CancellationTokenSource
I would like to suggest adding a method to CancellationTokenSource:
The method would work like Cancel, with a difference:
Alternatively, it could just be fire-and-forget:
Use case:
Whenever a CancellationTokenSource is cancelled, all registration callbacks are executed synchronously. While this is OK in most cases, I ran into the following scenario:
My problem is: in this scenario, how to guarantee that the client code won't ever block my thread for a significant amount of time?
The most obvious solution is to call Cancel in another thread:
This would work. However, I would end up queuing work to the threadpool everytime I cancel a token, even though most of the tokens won't have a callback registered. It feels wrong having to add a cost to the 99% case to handle the 1%.
The "CancelAsync" method can't be implemented from outside:
I don't see any major obstacle to implementation, and I think it can be done without adding an additional field to the class. That said, I'm aware of how specific is the scenario I described, and I don't know if many applications would benefit from this.
Past discussion
This was moved from https://github.com/dotnet/coreclr/issues/13494
Copy of the discussion:
davidfowl
This is similar to dotnet/corefx#14903 (but for the actual cancellation call, not dispose). I think you need to be clear if you want to
Sure it can be. See http://referencesource.microsoft.com/#System.Web/Util/CancellationTokenHelper.cs,cbfb354e962dfb2a for an example of how it could be done.
There are some questions I have based on this request
kevingosse
If I understand this code correctly, it always uses the threadpool to call
Cancel
. I was thinking of a way to use the threadpool only if there is a callback registered, and otherwise execute the call inline. The rationale being that in most cases there will be no callback (this is true for the codebase I work on, I'm not sure how representative it is).I don't need it in my scenario, that's why I also suggested the fire-and-forget version. In fact, I was first thinking of a
Cancel(bool executeCallbacksAsynchronously)
overload, but that's not possible because there's already aCancel(bool throwOnFirstException)
. Then I thought ofCancelAsync
. I'm not sure the task would actually be useful to anybody.Same answer as above.
The text was updated successfully, but these errors were encountered: