异步调用模式学习记录

    异步调用一般有BeginXXX和EndXXX这两种方法,开始调用BeginXXX方法时候,程序会立即响应,并抛出一线程,使当前线程继续
下去。在这里我为了简单,使用桌面线程为主线程,由她抛出一个子线程。在抛出之后,就会有两线程分享CPU时间片。
   什么时候知道异步完成了?这里就需要提到BeginXXX方法会返回的一个IAsyncResult对象,此对象跟踪异步调用的过程,提供状态信息,关于此点可以参考:http://www.microsoft.com/china/MSDN/library/archives/library/dnnetCOMp/html/netcfWebServices.asp#netcfwebservices_topic2

   现在重要说一下,BeginXXX方法传递一个回调委托,然后通过EndXXX响应的过程;有事例如下:

localhost.Service1 service=new localhost.Service1();  ///创建代理类
DataGrid dg;
private void SomeUIEvent( object sender, EventArgs e )
{
   // Create a callback delegate so we will
     
    AsyncCallback callBack = new
      AsyncCallback( DataCallback );

   // Start retrieving the customer data.
   service.BeginGetData( "User", callBack,  service);
}

public void DataCallback( IAsyncResult ar )
{
      // Retrieve the customer data.
      localhost.Service1 service=(localhost.Service1)ar.AsyncState;
      DataSet ds1 = service.EndGetData( ar );
     dg.DataSource=ds1;//注意
}

  
 在这里就完成异步,取的数据,,但是如果仔细看,,ds这个在桌面线程中创建的DataSet,可能被多个子线程同时使用,如果在某一线程对DataSe做出修改的同时,其它线程也去使用它,那么最后结果就不好断定,反正不是自己想要的!!
怎么解决这个问题,,我找到两种方法,一是使用线程的临界区,也就是使用lock,将要操作DataSet的代码区保护起来,保证每次只有一个线程进入此临界区,例如:
public void CustomerDataCallback( IAsyncResult ar )
{
  lock(this){
      // Retrieve the customer data.
      localhost.Service1 service=(localhost.Service1)ar.AsyncState;
      DataSet ds1 = service.EndGetData( ar );
      dg.DataSource=ds1;
   }
}
这里需要注意的,所有要使用dg的地方都要用lock,,,
这样好象就很麻烦!!!

另外一种是使用Control.Invoke方法,以实现线程的安全切换,比如:
public void DataCallback( IAsyncResult ar )
{
      // Retrieve the customer data.
      localhost.Service1 service=(localhost.Service1)ar.AsyncState;
      DataSet ds1 = service.EndGetData( ar );

      // Create an EventHandler delegate.
      EventHandler updateUI = new EventHandler( UpdateUI );

      // Invoke the delegate on the UI thread.
      this.Invoke( updateUI, new object[] { ds1, null } );
}

private void UpdateUI( object sender, EventArgs e )
{
      // Update the user interface.
      dg.DataSource = (DataSet)sender;
}


最后你可以把Console.WriteLine("thread {0} does some work.",
    AppDomain.GetCurrentThreadId());
放入以上方法中,,你会发现桌面线程和 UpdateUI所在的线程是一样的!!!!也就是说ds的获取是在桌面线程中完成的,所以也就不成在冲突了!!!

原文地址:https://www.cnblogs.com/pojia/p/315454.html