在.Net 4.5中,Stream类上存在一个方法,该方法在监视取消令牌的同时从流中异步读取。
ReadAsync :
buffer:byte[] *
offset:int *
count:int *
cancellationToken:CancellationToken -> Task<int>
如果另一个线程应首先触发取消令牌然后关闭流,那么是否保证在ReadAsync抛出异常之前取消读取线程?
我可以使用.Net 4.0框架和没有ReadAsync的F#异步工作流程(它没有接受要监视的取消令牌的过载)以某种方式实现此保证吗?
答案 0 :(得分:3)
除非您将多个任务链接在一起,否则此特定重载基本上无用 - cancellationToken
仅在进入ReadAsync
方法调用时检查,而不是在基础Stream.BeginRead
调用时检查执行。
从ILSpy转发的代码:
public virtual Task<int> ReadAsync(byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken)
{
if (!cancellationToken.IsCancellationRequested)
return this.BeginEndReadAsync(buffer, offset, count);
return Task.FromCancellation<int>(cancellationToken);
}
如您所见,cancellationToken
未转发至BeginEndReadAsync
来电,BeginEndReadAsync
仅以Stream.BeginRead
方式实施:
private Task<int> BeginEndReadAsync(byte[] buffer, int offset, int count)
{
return TaskFactory<int>.FromAsyncTrim<Stream, Stream.ReadWriteParameters>(
this,
new Stream.ReadWriteParameters
{
Buffer = buffer,
Offset = offset,
Count = count
},
(Stream stream, Stream.ReadWriteParameters args, AsyncCallback callback, object state) =>
stream.BeginRead(args.Buffer, args.Offset, args.Count, callback, state),
(Stream stream, IAsyncResult asyncResult) =>
stream.EndRead(asyncResult)
);
}
此时,您所拥有的唯一保证是派生流类型所做的那些,因类型而异。
请注意,这是基于当前的.Net 4.5位,并且实施当然可能会在未来发生变化。
答案 1 :(得分:1)
在使用Result
/ RunSynchronously
运行任务之前,您可以检查Task.IsCanceled
。
以下是一些示例代码:
use stream = new MemoryStream(Array.init 1000 (fun i -> byte (i % int Byte.MaxValue)))
use waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset)
use cts = new CancellationTokenSource()
let thread = Thread(fun () ->
Thread.Sleep(1000)
let buf = Array.zeroCreate 100
let task = stream.ReadAsync(buf, 0, buf.Length, cts.Token)
if not task.IsCanceled then task.RunSynchronously()
waitHandle.Set() |> ignore)
thread.Start()
cts.Cancel()
waitHandle.WaitOne() |> ignore
但是一旦ReadAsync
开始,将抛出一个AggregateException
,说明该任务已被取消。
答案 2 :(得分:0)
AFAIK,您必须使用互斥锁来保护流并使用取消令牌检查线程是否有待取消。 Stream
上没有异步原语可以为您处理此问题。