Gig*_*igi 13 c# task task-parallel-library cancellationtokensource
我有以下代码行用于从NetworkStream异步读取:
int bytesRead = await Task<int>.Factory.FromAsync(this.stream.BeginRead, this.stream.EndRead, buffer, 0, buffer.Length, null);
Run Code Online (Sandbox Code Playgroud)
我想支持取消.我看到我可以使用CancellationTokenSource取消任务,但是我没有看到任何方法可以将它传递给TaskFactory.FromAsync().
是否可以使FromAsync()构造的任务支持取消?
编辑:我想取消已经运行的任务.
Ste*_*eri 10
Gigi,遗憾的是FromAsync的语义特性表明你只是在调整异步过程到TPL的API(TPL = Microsoft的任务并行库)
本质上,TPL的ReadAsync控制异步行为本身,而FromAsync仅包装行为(但不控制它).
现在由于Cancellation是一个特定于TPL的构造,并且由于FromAsync无法控制被调用的异步方法的内部工作,因此无法保证干净地取消任务并确保所有资源都正确关闭(这就是为什么它如果你很好奇,只需反编译方法;))
在这些情况下,将实际的异步调用自己包装在正常任务中并检测OperationCancelled异常更有意义,这将使您有机会通过进行适当的调用来关闭流.
简而言之,答案是否定的,但没有什么可以阻止您创建一个通用的重载方法,该方法将根据其类型选择正确的策略来干净地关闭流.
正如其他人已经提到的那样,没有干净的方法来实现你所要求的.异步编程模型中没有取消的概念; 因此,它不能通过FromAsync
转换器进行改装.
但是,您可以为Task
包装异步操作引入取消.这不会取消基础操作本身 - 您NetworkStream
仍然会继续从套接字读取所有请求的字节 - 但它将允许您的应用程序作出反应,就像操作被取消一样,立即OperationCanceledException
从您的await
(并执行任何已注册的任务延续)中抛出一个.一旦完成,底层操作的结果将被忽略.
这是一个辅助扩展方法:
public static class TaskExtensions
{
public async static Task<TResult> HandleCancellation<TResult>(
this Task<TResult> asyncTask,
CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested.
// http://stackoverflow.com/a/18672893/1149773
var tcs = new TaskCompletionSource<TResult>();
cancellationToken.Register(() =>
tcs.TrySetCanceled(), useSynchronizationContext: false);
var cancellationTask = tcs.Task;
// Create a task that completes when either the async operation completes,
// or cancellation is requested.
var readyTask = await Task.WhenAny(asyncTask, cancellationTask);
// In case of cancellation, register a continuation to observe any unhandled
// exceptions from the asynchronous operation (once it completes).
// In .NET 4.0, unobserved task exceptions would terminate the process.
if (readyTask == cancellationTask)
asyncTask.ContinueWith(_ => asyncTask.Exception,
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return await readyTask;
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个使用扩展方法将操作视为300ms后取消的示例:
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(300));
try
{
int bytesRead =
await Task<int>.Factory.FromAsync(this.stream.BeginRead, this.stream.EndRead, buffer, 0, buffer.Length, null)
.HandleCancellation(cts.Token);
}
catch (OperationCanceledException)
{
// Operation took longer than 300ms, and was treated as cancelled.
}
Run Code Online (Sandbox Code Playgroud)
不,没有通用的方法来取消这样的任务.取消是特定于API的.
WebClient
有一个Cancel
方法.Socket
或者FileStream
需要Close
取消未完成的电话.这是因为IO操作的实现者必须支持取消.
使用NetworkStream.ReadAsync
和传递取消令牌似乎很诱人但是Stream.ReadAsync
.后者只是抛弃了令牌.基本上不支持.
Stream.ReadAsync
只是基类方法.它本身没有任何作用.具体IO操作仅由派生类别发布.那些必须支持原生取消.Stream无法阻止它们.碰巧NetworkStream
不支持取消.
我知道您要取消操作并保持套接字打开.但这是不可能的.(主观注意:这实际上是一种悲伤的状态.特别是考虑到Windows支持Win32级别的可取消IO.)
如果您仍希望应用程序快速继续,虽然IO操作不可取消,但只需忽略该任务的结果并继续.请注意,最终IO可能会完成,例如从套接字缓冲区中排出数据或导致其他副作用.
"忽略取消"有效地使流位置不确定.流变得无法使用.这并不能真正避免打开新流的需要.您仍然必须摆脱旧流(在大多数情况下)并重新打开.此外,您正在引入并发.