zoukankan      html  css  js  c++  java
  • 与其他.Net异步模式和类型进行互操作

    返回该系列目录《基于Task的异步模式--全面介绍》


    Tasks和异步编程模型APM(Tasks and the Asynchronous Programming Model)

    从APM到Tasks

    APM模式依赖两个对应的方法来表示一个异步操作:BeginMethodName和EndMethodName。在高级别,begin方法接受的参数和相应的同步方法MethodName的参数是一样的,而且还接受一个AsyncCallback和一个object state。begin方法然后返回IAsyncResult,IAsyncResult从它的AsyncState属性返回传递给begin方法的object state。异步操作完成时,IAsyncResult的IsCompleted属性会开始返回true,且会设置它的AsyncWaitHandle属性。而且,如果begin方法的AsyncCallback参数是非空的,那么会调用callback,且将它传给从begin方法返回的相同的IAsyncResult。当异步操作确实完成时,会使用EndMethodName方法连接该操作,检索任何结果或者强制产生的异常传播。

    由于APM模式结构的本质,构建一个APM的包装器来将它暴露为一个TAP实现是相当容易的。实际上,.Net Framework 4 以TaskFactory.FromAsync的形式提供了转化的帮助路线。

    思考.Net 中的Stream类和BeginRead/EndRead 方法,它们都代表了同步的Read方法的APM对应版本:

    public int Read(
        byte [] buffer, int offset, int count);
    …
    public IAsyncResult BeginRead(
        byte [] buffer, int offset, int count, 
        AsyncCallback callback, object state);
    public int EndRead(IAsyncResult asyncResult);

    利用FromAsycn,可实现该方法的TAP包装器:

    public static Task<int> ReadAsync(
        this Stream stream, byte [] buffer, int offset, int count)
    {
        if (stream == null) throw new ArgumentNullException(“stream”);
        return Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead,
            buffer, offset, count, null);
    }

    这个使用了FromAsync的实现和下面的具有同样效果:

    public static Task<int> ReadAsync(
        this Stream stream, byte [] buffer, int offset, int count)
    {
        if (stream == null) throw new ArgumentNullException(“stream”);
        var tcs = new TaskCompletionSource<int>();
        stream.BeginRead(buffer, offset, count, iar =>
        {
            try { tcs.TrySetResult(stream.EndRead(iar)); }
            catch(OperationCanceledException) { tcs.TrySetCanceled(); }
            catch(Exception exc) { tcs.TrySetException(exc); }
        }, null);
        return tcs.Task;
    }

    从Tasks到APM

    对于现有的基础设施期望代码实现APM模式的场合,能够采取TAP实现以及在期待TAP实现的地方使用它也是很重要的。幸好有了tasks的组合性,以及Task本身实现IAsyncResult的事实,使用一个简单的帮助函数就可以实现了(这里展示的是一个Task<TResult>的扩展,但几乎相同的函数可能用于非泛型的Task):

    public static IAsyncResult AsApm<T>(
        this Task<T> task, AsyncCallback callback, object state)
    {
        if (task == null) throw new ArgumentNullException(“task”);
        var tcs = new TaskCompletionSource<T>(state);
        task.ContinueWith(t =>
        {
            if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions)
            else if (t.IsCanceled) tcs.TrySetCanceled();
            else tcs.TrySetResult(t.Result);
    
            if (callback != null) callback(tcs.Task);
        }, TaskScheduler.Default);
        return tcs.Task;
    }

    现在,想一个有TAP实现的场合:

    public static Task<string> DownloadStringAsync(Uri url);

    且我们需要提供APM实现:

    public IAsyncResult BeginDownloadString(
        Uri url, AsyncCallback callback, object state);
    public string EndDownloadString(IAsyncResult asyncResult);

    可以通过下面代码实现:

    public IAsyncResult BeginDownloadString(
        Uri url, AsyncCallback callback, object state)
    {
        return DownloadStringAsync(url).AsApm(callback, state);
    }
    
    public string EndDownloadString(IAsyncResult asyncResult)
    {
        return ((Task<string>)asyncResult).Result;
    }

    Tasks和基于事件的异步模式EAP(Event-based Asynchronous Pattern)

    基于事件的异步模式依赖于一个返回void的实例MethodNameAsync方法,接收和同步方法MethodName方法相同的参数,并且要实例化异步操作。实例异步操作之前,事件句柄使用相同实例上的事件注册,然后触发这些事件来提供进度和完成通知。事件句柄一般都是自定义的委托类型,该委托类型利用了派生自ProgressChangedEventArgs或AsyncCompletedEventArgs的事件参数类型。

    包装一个EAP实现更复杂一些,因为该模式本身牵扯了比APM模式更多的变量和更少的结构。为了演示,接下来包装一个DownloadStringAsync方法。DownloadStringAsync接受一个Uri参数,为了上报多个进度上的统计数据,下载时会触发DownloadProgressChanged 事件,完成时会触发DownloadStringCompleted 事件。最终结果是一个包含在指定Uri的页面内容的字符串。

    public static Task<string> DownloadStringAsync(Uri url)
    {
        var tcs = new TaskCompletionSource<string>();
        var wc = new WebClient();
        wc.DownloadStringCompleted += (s,e) =>
        {
            if (e.Error != null) tcs.TrySetException(e.Error);
            else if (e.Cancelled) tcs.TrySetCanceled();
            else tcs.TrySetResult(e.Result);
        };
        wc.DownloadStringAsync(url);
        return tcs.Task;
    }

    Tasks和等待句柄(WaitHandlers)

    从WaitHandlers到Tasks

    高级的开发人员可能会发现,WaitHandle 设置时,自己利用 WaitHandles 和线程池的 RegisterWaitForSingleObject 方法进行异步通知,然而这本质上不是一个异步模式 。我们可以包装RegisterWaitForSingleObject来启用WaitHandle之上的任何异步等待的基于task的选择:

    public static Task WaitOneAsync(this WaitHandle waitHandle)
    {
        if (waitHandle == null) throw new ArgumentNullException("waitHandle");
    
        var tcs = new TaskCompletionSource<bool>();
        var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, 
            delegate { tcs.TrySetResult(true); }, null, -1, true);
        var t = tcs.Task;
        t.ContinueWith(_ => rwh.Unregister(null));
        return t;
    }

    使用那些之前演示的构建于Task之上的数据结构的技巧,相似地,构建一个不依赖WaitHandles且完全以Task的角度工作的异步信号灯(semaphore)也是可能的。事实上,.Net 4.5中的SemaphoreSlim 类型暴露了开启这个的WaitAsync方法。

    比如,之前提到的System.Threading.Tasks.Dataflow.dll中的BufferBlock<T>类型可以这样使用:

    static SemaphoreSlim m_throttle = new SemaphoreSlim(N, N);
    
    static async Task DoOperation()
    {
        await m_throttle.WaitAsync();
        … // do work
        m_throttle.Release ();
    }

    从Tasks到WaitHandlers

    如之前提到的,Task类实现了IAsyncResult,该IAsyncResult的实现暴露了一个返回WaitHandle的AsycnWaitHandle属性,此WaitHandle是在Task完成时设置的。照这样,获得一个Task的WaitHandle可以像下面这样实现:

    WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;

    返回该系列目录《基于Task的异步模式--全面介绍》


  • 相关阅读:
    【STM32F429】第5章 ThreadX NetXDUO网络协议栈介绍
    【STM32H7】第5章 ThreadX NetXDUO网络协议栈介绍
    【STM32F407】第5章 ThreadX NetXDUO网络协议栈介绍
    【STM32H7】第25章 ThreadX GUIX复选框Checkbox回调事件处理
    【STM32F429】第23章 ThreadX GUIX复选框Checkbox回调事件处理
    【STM32H7】第4章 初学ThreadX NetXDUO准备工作
    【STM32F429】第4章 初学ThreadX NetXDUO准备工作
    【STM32F407】第4章 初学ThreadX NetXDUO准备工作
    【STM32H7】第3章 PHY芯片和STM32的MAC基础知识
    【STM32F429】第3章 PHY芯片和STM32的MAC基础知识
  • 原文地址:https://www.cnblogs.com/farb/p/4903213.html
Copyright © 2011-2022 走看看