异步操作(三)

  APM的轮询聚焦技巧

  就从字面意思来理解,每隔一段时间来查询,异步操作的结果。而怎么实现轮询的方法了,这里就要谈到IAsyncResult接口。它定义了若干个只读属性

 

 
  
public interface IAsyncResult
{
Object AsyncState {
get ;}
WaitHandle AsyncWaitHandle {
get ;}
Boolean IsCompleted {
get ;}
Boolean CompletedSynchronously {
get ;}
}

其中最常用的属性是AsyncState。使用轮询聚集技巧时,要使用AsyncWaitHandle和IsCompleted属性。而对于CompletedSynchronously属性,有时侯供实现BeginXxx和EndXxx方法的开发人员查询。其实轮询的效率不高,编写轮询聚集技巧的代码时,要让一个线程定期地询问CLR来查看异步请求是否已经完成运行。实质上是浪费了一个线程的时间。

下面是一个轮询聚集技巧的示例:

 
  
public static void PollingWithIsCompleted()
{
FileStream fs
= new FileStream(@”C:\Boot.ini”,FileMode.Open,FileAccess.Read,
FileShare.Read,
1024 ,FileOptions.Asynchronous);
Byte[] data
= new Byte[ 100 ];

// 为FileStream对象初始化一个异步读操作
IAsyncResult ar = fs.BeginRead(data, 0 ,data.Length, null , null );

// 每隔10秒查询一次是否操作已经完成
while ( ! ar.IsCompleted)
{
Console.WriteLine(“Operation not completed;still waiting.”);
Thread.Sleep(
10 );
}

// 获取结果。注意:EndRead方法不能挂起这个线程 因为前面那个循环已经确定操作完成了
Int32 bytesRead = fs.EndRead(ar);

fs.Close();

// 显示结果
….
}

上面是通过IAysncResult的IsCompleted属性来判断一个操作是否完成,下面是通过AsyncWaitHandle属性,它返回一个派生自WaitHandle的对象的引用,该对象通常为System.Threading.ManualResetEnent.

 
  
public static void PollingWithAsyncWaitHandle()
{
// 打开指示异步I/O操作的文件
FileStream fs = new FileStream(@”C:\Boot.ini”,FileMode.Open,FileAccess.Read,
FileShare.Read,
1024 ,FileOptions.Asynchronous);
Byte[] data
= new Byte[ 100 ];

// 为FileStream对象初始化一个异步读操作
IAsyncResult ar = fs.BeginRead(data, 0 ,data.Length, null , null );

// 与上面例子效果相同
While( ! ar.AsyncWaitHandle.WaitOne( 10 , false )
{
Console.WriteLine(“Operation not completed;still waiting.”);
}

// 获取结果。注意:EndRead方法不能挂起这个线程 因为前面那个循环已经确定操作完成了
Int32 bytesRead = fs.EndRead(ar);

fs.Close();

// 显示结果
….

}

APM方法回调聚集技巧

  在所有ARM聚集技巧中,构建性能和扩展的应用程序框架时,方法回调聚集技巧最好用。该技巧不会将一个线程置于等待状态,而且该技巧还不会定期地检查异步操作是否完成而浪费CPU时间。

  首先将异步I/O请求排队等候,然后线程继续执行它希望执行的任何事情。接着当 I/O请求完成时,Windows将工作项加入CLR的线程池的队列中。最后,线程池中的线程将工作项从队列中取出,并调用我们编写的一些方法(通过这种方式我们可以知道异步I/O操作已经完成)。现在回调内部,我们首先调用EndXxx方法来获得异步操作的结果,然后就可以自由的继续处理结果。当回调方法返回时,线程池中的线程返回到线程池中准备服务下一个排队的工作项。

 
  
public static class Program
{
// 将数组声明为static,以便Main方法和ReadIsDone方法可访问它
private static Byte[] s_data = new Byte[ 100 ];

public static void Main()
{
Console.WriteLine(“Main thread ID
= { 0 }”,Thread.CurrentThread.ManagedThreadId);

// 打开指示异步I/O操作的文件
FileStream fs = new FileStream(@”C:\Boot.ini”,FileMode.Open,FileAccess.Read,
FileShare.Read,
1024 ,FileOptions.Asynchronous);

// 为FileStream对象初始化一个异步读操作,并将FileStream对象fs传递给回调方法ReadISDone
fs.BeginRead(s_data, 0 ,s_data.Length,ReadIsDone,fs);

// 在这里执行一些其他代码将非常有用…..

// 出于演示目的,将主线程挂起
Console.ReadLine();

}

private static void ReadIsDone(IAsyncResult ar)
{
// 显示正在执行ReadIsDone方法线程的ID
Console.WriteLine(“ReadIsDone thread Id = { 0 }”,
Thread.CurrentThread.ManagedThreadID

// 从IAsyncResult对象中提取FileStream对象
FileStream fs = (FileStream)ar.AsyncState;

// 获取结果
Int32 bytesRead = fs.EndRead(ar);

// 已经没有操作执行任务,关闭文件
fs.Close();

// 显示结果
}
}

因为这里ReadIsDone函数,要通过全局变量才能访问到读取到的数据。都写到Main函数当中去。还有就是要将回调方法名称传递给BeginRead函数以及FileStream对象实例也要传递给该函数(目的通过这种方式将FileStream对象实例传递到了回调方法中)。当然这里可以利用C#匿名方法特征,就不存在这个问题了。

 
  
public static void Main()
{
Console.WriteLine(“Main thread ID
= { 0 }”,Thread.CurrentThread.ManagedThreadId);

// 打开指示异步I/O操作的文件
FileStream fs = new FileStream(@”C:\Boot.ini”,FileMode.Open,FileAccess.Read,
FileShare.Read,
1024 ,FileOptions.Asynchronous);
Byte[] data
= new Byte[ 100 ];


// 为FileStream对象初始化一个异步读操作,并将FileStream对象fs传递给回调方法ReadISDone
fs.BeginRead(s_data, 0 ,s_data.Length, delegate (IAsyncResult ar)
{
// 显示正在执行ReadIsDone方法线程的ID
Console.WriteLine(“ReadIsDone thread Id = { 0 }”,
Thread.CurrentThread.ManagedThreadID

// 获取结果
Int32 bytesRead = fs.EndRead(ar);

// 已经没有操作执行任务,关闭文件
fs.Close();

// 显示结果

},
null );

// 出于演示目的,将主线程挂起
Console.ReadLine();
}

还记得异步操作(二)中最后一个多流读取文件的例子吗,这里改为回调聚集技巧,那么程序效率又得到进一步提高

 
  
private static void ReadMultipleFiles( params String[] pathnames)
{
for (Int32 n = 0 ;n < pathname.Length;n ++ )
{
// 打开指示异步I/O操作文件
Sream stream = new FileStream(pathnames[n],FileMode.Open,FileAccess.Read,
FileShare.Read,
1024 ,FileOptions.Asynchronous);

// 为Stream 对象初始化一个异步操作
new AsyncStreamRead(stream, 100 , delegate (Byte[] data
{
// 处理数据

});
}

// 所有的流都已经打开了,而且所有的读请求都已经排队;他们同时并发执行,
// 而且他们结束时它们将被处理

// 主线程可以在这里做一些其他工作,如果愿意的话

// 处于演示目的,将主线程挂起来
Console.ReadLine();

}
}

// 定义一个委托
private delegate void StreamBytesRead(Byte[] streamData);

private sealed class AsyncSreamRead
{
private Stream m_stream;
private Byte[] m_data;
StreamBytesRead m_callback;

public AsyncStreamRead(Stream stream,Int32 numBytes,StreamByteRead callback)
{
m_stream
= stream;
m_data
= new Byte[numBytes];
m_callback
= callback;

stream.BeginRead(m_data,
0 ,numBytes,ReadIsDone, null );
}

// 当IO操作结束时调用下述方法
private void ReadIsDone(IAsyncResult ar)
{
Int32 numByteRead
= m_stream.EndRead(ar);

m_stream.Close();

// 调整数据大小以节省空间
Array.Resize( ref m_data,numBytesRead);

// 调用应用程序的回调方法(注意这里调用的方法是个匿名方法 )
m_callback(m_data);
}
}

使用APM执行受计算限制的异步操作

先看一个简单的程序

 
  
private static UInt64 Sum(UInt64 n)
{
UInt64 sum
= 0 ;
for (UInt64 i = 1 ;i <= n;i ++ )
{
// 检查是否益处,是就抛出一个异常
Checked
{
sum
+= I;
}
}
return sum;
}

如果这里n非常大,Sum方法将需要很长时间来执行。那么这里也用异步操作来完成

 
  
internal delegate UInt64 SumDelegate(UInt64 n);

这里定义委托,据委托揭秘可知,在编译器里面实际的内容

 
  
internal sealed class SumDelegate:MulticastDelegate
{
public SumDelegate(Object object ,IntPtr method);
public UInt64 Invoke(UInt64 n);
pulbic IAsyncResult BeginInvoke(UInt64 n,AsyncCallback Callback,Object
object );
public UInt64 EndInvoke(IAsyncResult result);
}

相信看到这段代码,就应该会写后面的程序了:

 
  
public static void Main()
{
SumDelegate sumDelegate
= Sum;
// 调用线程池线程
sumDelegate.BeginInvoke( 100000000 ,SumIsDone,sumDelegate);
Console.ReadLine();
}

 

 

 

这里说明一下委托的BeginInvoke方法通过内部调用ThreadPool的QueueUserWorkItem将受限制的操作加入到CLR线程池的队列中。最后,BeginInvoke方法将IAsyncResult对象返回到它的调用者。调用者在执行异步I/O操作时就可以使用这个IAsyncResult对象了。因为BeginInvoke方法将操作加入线程池的队列,所以线程池中的线程将被唤醒,将工作项从队列中取出,然后调用受计算限制的操作。线程池中的线程从执行方法的过称中返回时,线程就返回到线程池。但是本例方法中有SumIsDone,那么该线程就会调用SumIsDone方法。即完成回调。

 
  
private static void SumIsDone(IAsyncResult)
{
// 从IAsyncResult对象中提取SumDelegate对象
SumDelegate sumDelegate = (SumDelegate)ar.AsyncState;
// 获取结果
UInt64 sum = sumDelegate.EndInvoke(ar);
// 显示结果
Console.WriteLine(“Sum = { 0 }”,sum);
}

 

本文知识来源:《CLR Via C#》

转载于:https://www.cnblogs.com/couhujia/archive/2010/05/08/1730384.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值