Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
512 views
in Technique[技术] by (71.8m points)

c# - Does the Rx library omits disposing of the CancellationTokenSources it creates?

The Rx library includes operators that accept lambda parameters, and some of these lambdas are provided with a CancellationToken that is controlled by the library itself. Some examples of these operators are the FromAsync, StartAsync and Create:

// Converts an asynchronous action into an observable sequence. Each subscription
// to the resulting sequence causes the action to be started. The CancellationToken
// passed to the asynchronous action is tied to the observable sequence's subscription
// that triggered the action's invocation and can be used for best-effort cancellation.
public static IObservable<Unit> FromAsync(Func<CancellationToken, Task> actionAsync);

I was under the impression that the Rx library does a good job at managing the lifecycle of the CancellationTokenSources that obviously has to create behind the scenes, but I am not so sure any more. Let's first state that the documentation insists strongly that the CancellationTokenSources should be disposed of:

This type implements the IDisposable interface. When you have finished using an instance of the type, you should dispose of it either directly or indirectly. To dispose of the type directly, call its Dispose method in a try/catch block. To dispose of it indirectly, use a language construct such as using (in C#) or Using (in Visual Basic).

Also from here:

Always call Dispose before you release your last reference to the CancellationTokenSource. Otherwise, the resources it is using will not be freed until the garbage collector calls the CancellationTokenSource object's Finalize method.

I made the experiment below to test my assumptions. It uses reflection to read the private fields _source and _disposed of the types CancellationToken and CancellationTokenSource respectively (.NET 5).

CancellationToken capturedToken = default;
var subscription = Observable.FromAsync(async token =>
{
    capturedToken = token;
    token.Register(() => Console.WriteLine("Token canceled"));
    await Task.Delay(Timeout.Infinite, token);
})
.TakeUntil(Observable.Timer(TimeSpan.FromMilliseconds(500)))
.Finally(() => Console.WriteLine("The observable was terminated"))
.Subscribe();

Thread.Sleep(1000);

var cts = (CancellationTokenSource)(typeof(CancellationToken)
    .GetField("_source", BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(capturedToken));
bool disposed = (bool)(typeof(CancellationTokenSource)
    .GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(cts));
Console.WriteLine($"IsCancellationRequested: {cts.IsCancellationRequested}");
Console.WriteLine($"IsDisposed: {disposed}");

Output:

Token canceled
The observable was terminated
IsCancellationRequested: True
IsDisposed: False

Try it on Fiddle (.NET Framework version, having differently named private fields)

The captured CancellationToken is inspected half a second after the asynchronous operation has been canceled and the observable has terminated. The _disposed field has the value false, indicating that the Dispose method of the associated CancellationTokenSource has not been invoked. Am I doing something wrong, or the Rx library indeed omits disposing of the CancellationTokenSources it creates?

.NET 5.0.1, System.Reactive 5.0.0, C# 9

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...