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
835 views
in Technique[技术] by (71.8m points)

.net - cancellationtoken timeout vs task.delay() and timeout

I want to run an operation that should timeout after n milliseconds. I've implemented it two ways, one by cancelling the operation myself after waiting n milliseconds, and one by passing in a CancellationToken set to expire after n milliseconds.

I'm worried that as my system goes under heavy load, the cancellationtoken could expire before the operation even starts. It seems that if I implement the timeout myself using Task.Delay(), then the Delay() call won't run until after my operation begins.

Here's how I'm doing it:

public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout)
    {
        Task completedTask = await Task.WhenAny(task, Task.Delay(timeout));
        if (completedTask == task)
        {
            return await task;
        }

        throw new TimeoutException();
    }

// Use it like this
await SomeOperationAsync().TimeoutAfter(TimeSpan.FromMilliseconds(n));

Compared to:

CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromMilliseconds(n));
await SomeOperationAsync(source.Token);
See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

I'm not sure that your worry is justified, but I'm going to assume that it is.

The problem with your code that uses Task.Delay() is that you're not actually canceling the operation. This might mean that you're wasting resources or misinforming your users (you tell them that the operation timed out, while the operation is still running and will most likely complete successfully).

Now, if you want to make sure that the cancellation token starts ticking only after the operation is started, then do that:

var source = new CancellationTokenSource();
var task = SomeOperationAsync(source.Token);
source.CancelAfter(TimeSpan.FromMilliseconds(n));
await task;

If you do this often, you might want to encapsulate this logic into a method (might need a better name):

public static async Task WithTimeoutAfterStart(
    Func<CancellationToken, Task> operation, TimeSpan timeout)
{
    var source = new CancellationTokenSource();
    var task = operation(source.Token);
    source.CancelAfter(timeout);
    await task;
}

Usage:

await WithTimeoutAfterStart(
    ct => SomeOperationAsync(ct), TimeSpan.FromMilliseconds(n));

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

...