自定义AsyncResult

来源:互联网 发布:企业家精神 知乎 编辑:程序博客网 时间:2024/06/07 01:53

.net中提供的APM编程模型主要为了实现两个需求:

1.      非阻塞模型。

例如一个UI客户端要运行一个复杂算法得到一个结果,这时候UI最好是不能卡住的,所以就需要使用APM模式。

2.      节约线程资源

在使用同步API来访问网络的时候,在结果返回之前,无论是UI线程还是线程池线程,客户端会有一个UI线程在等待。此线程浪费了内存资源,所以最好应该避免这么做,这时候也需要使用APM编程模式了。

大部分情况下,都不需要自己来定义AsyncResult。可以直接使用系统提供的API来得到一个实现了IasyncResult的对象。任何委托对象都支持APM模式,如下:

Func<string>f =new Func<string>(()=>"1");

f.BeginInvoke(new AsyncCallback(asyncRet=>

    {

        strings = f.EndInvoke(asyncRet);

        Console.WriteLine(s);

    }), null);

使用BeginInvoke发起调用,使用EndInvoke方法得到结果,Callback委托大部分情况下都不会在调用BeginInvoke的线程上被调用。

很多访问网络和文件的类也提供了支持APM的API,这些API都是以Begin和End开头。使用这些API,就可以得到很高的性能,因为这些API不会阻塞你的线程,也不会占用任何线程用来等待,线程只会去做有用的工作,而不会用于等待。

什么时候需要自己来实现APM模式呢?考虑如下情况:

有10个客户端每隔一秒钟就去服务端查询有没有属于自己的事件发生。如果没有事件就返回。这时候会发生什么状况?服务端的压力很变的很大,每秒钟会有10个请求发生,假如有1000个客户端,而服务端又什么事件都没有,那么每秒钟服务端要处理1000个请求。而这1000个请求没有返回任何有意义的内容。

一个改进的策略是,每次客户端查询的时候,服务端发现没有任何事件属于该客户端,那么就让此请求等待10秒钟,如果10秒内有事件发生,那么离开返回,如果到了第10秒,还没有事件发生,那么就返回一个空列表。在这种方案下,假如10秒内没有任何事件发生,那么每10秒内会有1000个请求。显然这种方案更好一些,但是请考虑,如果服务端采用同步模型,在每个请求所等待的10秒内,都会占用一个线程,那么在10秒内会有1000个线程在等待。按照1个线程占用1mb内存计算,1000个客户端轮询要至少占用1G内存。而这些占用内存的线程所做的事情只有等待。这显然是不合理的。

所以一个更好的方案是,在这1000个请求等待的10秒内,不要有任何线程在等待。这正是APM模式能够完成的,只是我们需要自定义一个实现了IAsyncResult的类。

public class AsyncResult: IAsyncResult, IDisposable

{

  privateconstintStatePending = 0;

  privateconstintStateCompletedSynchronously = 1;

  privateconstintStateCompletedAsynchronously = 2;

 

  privatereadonlyAsyncCallback_asyncCallback;

  privatereadonlyobject_asyncState;   

  privateint _completedState = StatePending;

 

  privateManualResetEvent _asyncWaitHandle;

  privateException _exception;

  privateobject _result;

 

  // asyncCallbackstate都是APM模式需要的,我们需要保存它们。

  publicAsyncResult(AsyncCallback asyncCallback,object state)

  {

    _asyncCallback =asyncCallback;

    _asyncState = state;

  }

 

  // 当任务完成的时候调用此方法,在我们的例子中,有三种情况需要调用此方法。

1.  当获取事件的过程中出现异常的时候。

2.  当在10秒内有事件发生。此时需要将事件内容返回给客户端。

3.  当第10秒还没有事件发生,将返回客户端一个空列表。

当客户端发起查询时已经有事件存在,那么就可以直接在调用者线程完成任务并调用此方法,然后为completedSynchronously参数传递true。此参数告诉调用者任务在调用者线程完成。

当为exception参数传递了非空值的时候,忽略result参数。

  public void Complete(objectresult,Exception exception, bool completedSynchronously)

    {

      intprevState = Interlocked.Exchange(ref _completedState,

        completedSynchronously? StateCompletedSynchronously : StateCompletedAsynchronously);

     

      //对于同一个AsyncResult对象,此方法只能被调用一次。

      if(prevState != StatePending)

      {

        throw newInvalidOperationException("You can call Complete only once.");

      }

 

      _result = result;

      _exception = exception;

 

      //If the event exists, set it

      if(_asyncWaitHandle != null)

      {

        _asyncWaitHandle.Set();

      }

 

      //If a callback method was set, call it

      if(_asyncCallback != null)

      {

        _asyncCallback(this);

      }

    }

 

   //此方法用于让APM的使用者得到返回值。如果调用此方法的时候任务还未完成,那么将会阻塞当前线程,直到调用返回为止。大部分情况下都应该在callback委托里面调用APM中的End方法,以避免线程阻塞。

    publicvirtualobjectEndInvoke()

    {

      //This method assumes that only 1 thread calls EndInvoke for this object

      if(!IsCompleted)

      {

        // If the operation isn't done, wait for it

       AsyncWaitHandle.WaitOne();

       AsyncWaitHandle.Close();

        _asyncWaitHandle = null;       

      }

      //Operation is done: if an exception occured, throw it

      if(_exception != null)

      {

        throw _exception;

      }

 

      return_result;

    }

 

    #region Implementation of IAsyncResult

 

    publicobject AsyncState

    {

      get{ return _asyncState; }

    }

 

    publicbool CompletedSynchronously

    {

      get{ returnThread.VolatileRead(ref _completedState) == StateCompletedSynchronously;}

    }

 

//大部分情况下都不应该访问此属性。创建内核对象会影响性能。

    publicWaitHandle AsyncWaitHandle

    {

      get

      {

        if (_asyncWaitHandle == null)

        {

          bool done = IsCompleted;

          ManualResetEvent evt = newManualResetEvent(done);

         

          if (Interlocked.CompareExchange(ref _asyncWaitHandle, evt,null)!= null)

          {

            // Another thread created this object's event; dispose theevent we just created

            evt.Close();

          }

          else

          {

            if (!done && IsCompleted)

            {

              // If the operation wasn't done when we created the eventbut now it is done, set the event

             _asyncWaitHandle.Set();

            }

          }

        }

 

        return _asyncWaitHandle;

      }

    }

 

    publicbool IsCompleted

    {

      get

      {

        return Thread.VolatileRead(ref _completedState) != StatePending;

      }

    }

 

    #endregion 

  //如果没有在任务完成之前调用End方法,以及访问AsyncWaitHandle属性,就不需要调用Dispose

    publicvoid Dispose()

    {

      if(_asyncWaitHandle != null)

      {

       _asyncWaitHandle.Close();

      }

    }

 }

 

以上实现其实是copy自msdn的某blog,做了一些轻微的改动。

使用AsyncResult:

1.      按照APM模式定义自己的Begin,End方法对。

2.      在Begin方法内部,构造AsyncResult对象,并返回。

3.      开始自己的任务,任务应该持有AsyncResult对象。

4.      当任务完成后,调用AsyncResult对象的Complete方法。


0 0
原创粉丝点击