Spa*_*man 1 c# cancellation async-await cancellationtokensource cancellation-token
对于在以下情况下如何实现取消标记,我有些困惑。
说我有一个方法,它有一个取消令牌,没有指定超时,像这样。
public static async Task DoSomeAsyncThingAsync(CancellationToken cancellationToken = default)
{
try
{
Task.Delay(1000, cancellationToken)
}
catch (OperationCanceledException canceledException)
{
// Do something with canceledException
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", canceledException);
throw;
}
catch (Exception exception)
{
// Do something with exception
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", exception);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
但是在该方法中,我想调用另一个期望CancellationToken除此以外的方法,但这次我要对此设置超时。
public static async Task DoSomeAsyncThingAsync(CancellationToken cancellationToken = default)
{
try
{
var innerCancellationTokenSource = new CancellationTokenSource();
innerCancellationTokenSource.CancelAfter(1000);
var innerCancellationToken = innerCancellationTokenSource.Token;
await DoSomeElseAsyncThingAsync(innerCancellationToken);
}
catch (OperationCanceledException canceledException)
{
// Do something with canceledException
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", canceledException);
throw;
}
catch (Exception exception)
{
// Do something with exception
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", exception);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
我如何innerCancellationToken从cancellationToken参数中获得取消请求的尊重?
我能想到的最好的是这样的:
public static async Task DoSomeAsyncThingAsync(CancellationToken cancellationToken = default)
{
try
{
await Task.WhenAny(
DoSomeElseAsyncThingAsync(cancellationToken),
KaboomAsync(100, cancellationToken)
);
}
catch (OperationCanceledException canceledException)
{
// Do something with canceledException
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", canceledException);
throw;
}
catch (Exception exception)
{
// Do something with exception
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", exception);
throw;
}
}
public static async Task KaboomAsync(int delay, CancellationToken cancellationToken = default)
{
await Task.Delay(delay, cancellationToken);
throw new OperationCanceledException();
}
Run Code Online (Sandbox Code Playgroud)
但是,这并不完全正确。该KaboomAsync()功能将始终崩溃,而且这条路似乎很粗糙。有更好的模式吗?
发布答案我创建了此静态Util方法,以节省我将样板放入一百万次的麻烦。
希望它对某人有用。
public static async Task<T> CancellableUnitOfWorkHelper<T>(
Func<CancellationToken, Task<T>> unitOfWordFunc,
int timeOut,
CancellationToken cancellationToken = default
)
{
try
{
var innerCancellationTokenSource = new CancellationTokenSource(timeOut);
using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(innerCancellationTokenSource.Token, cancellationToken))
return await unitOfWordFunc(linkedTokenSource.Token);
}
catch (OperationCanceledException canceledException)
{
Console.WriteLine(
cancellationToken.IsCancellationRequested
? "Manual or parent Timeout {0}"
: "UnitOfWork Timeout {0}"
, canceledException
);
throw;
}
catch (Exception exception)
{
Console.WriteLine("Exception {0}", exception);
throw;
}
}
public static async Task CancellableUnitOfWorkHelper(
Func<CancellationToken, Task> unitOfWordFunc,
int timeOut,
CancellationToken cancellationToken = default
)
{
try
{
var innerCancellationTokenSource = new CancellationTokenSource(timeOut);
using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(innerCancellationTokenSource.Token, cancellationToken))
await unitOfWordFunc(linkedTokenSource.Token);
}
catch (OperationCanceledException canceledException)
{
Console.WriteLine(
cancellationToken.IsCancellationRequested
? "Manual or parent Timeout {0}"
: "UnitOfWork Timeout {0}"
, canceledException
);
throw;
}
catch (Exception exception)
{
Console.WriteLine("Exception {0}", exception);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
它们可以像这样使用。
await Util.CancellableUnitOfWorkHelper(
token => Task.Delay(1000, token),
200
);
Run Code Online (Sandbox Code Playgroud)
要么
await Util.CancellableUnitOfWorkHelper(
token => Task.Delay(1000, token),
200,
someExistingToken
);
Run Code Online (Sandbox Code Playgroud)
在这两个示例中,它将在200毫秒后超时,但第二个示例还将遵循“ someExistingToken”令牌的手动取消或超时。
CancellationTokenSource 有专门针对这种情况的方法: CreateLinkedTokenSource
在您的示例中,它可能看起来像这样:
public static async Task DoSomeAsyncThingAsync(CancellationToken cancellationToken = default)
{
try
{
var innerCancellationTokenSource = new CancellationTokenSource();
using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(innerCancellationTokenSource.Token, cancellationToken))
{
innerCancellationTokenSource.CancelAfter(1000);
await DoSomeElseAsyncThingAsync(linkedTokenSource.Token);
}
}
catch (OperationCanceledException canceledException)
{
// Do something with canceledException
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", canceledException);
throw;
}
catch (Exception exception)
{
// Do something with exception
Console.WriteLine("DoSomeElseAsyncThingAsync {0}", exception);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,布置链接源很重要,否则来自父令牌源的引用将防止其被垃圾回收。
另请参见区分取消和超时以及何时处置CancellationTokenSource的任何方法?
| 归档时间: |
|
| 查看次数: |
52 次 |
| 最近记录: |