模拟ReadAsync

时间:2012-09-26 19:08:44

标签: .net asynchronous f#

在.Net 4.5中,Stream类上存在一个方法,该方法在监视取消令牌的同时从流中异步读取。

ReadAsync : 
       buffer:byte[] * 
       offset:int * 
       count:int * 
       cancellationToken:CancellationToken -> Task<int>

如果另一个线程应首先触发取消令牌然后关闭流,那么是否保证在ReadAsync抛出异常之前取消读取线程?

我可以使用.Net 4.0框架和没有ReadAsync的F#异步工作流程(它没有接受要监视的取消令牌的过载)以某种方式实现此保证吗?

3 个答案:

答案 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上没有异步原语可以为您处理此问题。