异步调用一般有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的获取是在桌面线程中完成的,所以也就不成在冲突了!!!