异步编程(2)——.NET的异步编程模型

32 篇文章 0 订阅

APM支持三种聚集的技巧:等待直到完成,轮询和方法回调,下面追一介绍这几种技巧:

1.等待直至完成

using  System;
using  System.IO;
using  System.Threading;
public   static   class  Program  {
public static void Main() {
FileStream fs 
= new FileStream(@"C:Boot.ini", FileMode.Open,
FileAccess.Read, FileShare.Read, 
1024,
FileOptions.Asynchronous);
Byte[] data 
= new Byte[100];

//异步读取文件,所有的BeginXXX方法都会返回IAsyncResult类型的变量
IAsyncResult ar = fs.BeginRead(data, 0, data.Length, nullnull);

//执行一些其他操作

Int32 bytesRead 
= fs.EndRead(ar);
fs.Close();
Console.WriteLine(
"Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(data, 
0, bytesRead));
}

}


// 输出
5B - 62 - 6F - 6F - 74 - 20 - 6C - 6F - 61 - 64 - 65 - 72 - 5D - 0D - 0A - 74 - 69 - 6D - 65 - 6F -
75 - 74 - 3D - 33 - 30 - 0D - 0A - 64 - 65 - 66 - 61 - 75 - 6C - 74 - 3D - 6D - 75 - 6C - 74 - 69 -
28 - 30 - 29 - 64 - 69 - 73 - 6B - 28 - 30 - 29 - 72 - 64 - 69 - 73 - 6B - 28 - 30 - 29 - 70 - 61 -
72 - 74 - 69 - 74 - 69 - 6F - 6E - 28 - 31 - 29 - 5C - 57 - 49 - 4E - 44 - 4F - 57 - 53 - 0D - 0A -
5B
- 6F - 70 - 65 - 72 - 61 - 74 - 69 - 6E - 67 - 20 - 73 - 79 - 73 - 74 - 65 - 6D - 73 - 5D - 0D

 注意上面的调用,如果执行到ENDXXX,文件还没有读取完成,那么将会挂起调用线程,直至方法返回结果。下面看一下一个改进的版本,这个版本可以从多个文件,套接字,甚至串口中读取字节:

private   static   void  ReadMultipieFiles( params  String[] pathnames)  {

AsyncStreamRead[] asrs 
= new AsyncStreamRead[pathnames.Length];

for (Int32 n = 0; n < pathnames.Length; n++{
Stream stream 
= new FileStream(pathnames[n], FileMode.Open,FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous);
asrs[n] 
= new AsyncStreamRead(stream, 100);
}


for (Int32 n = 0; n < asrs.Length; n++{
Byte[] bytesRead 
= asrs[n].EndRead() ;
Console.WriteLine(
"Number of bytes read={0}", bytesRead.Length);
Console.WriteLine(BitConverter.ToString(bytesRead));
}

}


private   sealed   class  AsyncStreamRead  {

private Stream m_stream;
private IAsyncResult m_ar;
private Byte[] m_data;

public AsyncStreamRead(Stream stream, Int32 numBytes) {
m_stream 
= stream;
m_data 
= new Byte[numBytes];
m_ar 
= stream.BeginRead(m_data, 0, numBytes, nullnull);
}


public Byte[] EndRead() {
Int32 numBytesRead 
= m_stream.EndRead(m_ar);
m_stream.Close();
Array.Resize(
ref m_data, numBytesRead);
return m_data;
}

}

上面的方法还有一个低效的地方就是在第二个循环,我们要按顺序调用ENDXXX方法,但是每个文件的大小不一样,那么他的读取完成时间就会不同,如果前面有一个非常大的文件,而后面的文件比较小,我们在等待大文件读取完毕的时候,他后面的文件已经读取完了,但是我们还要等待这个大文件读取完了才能显示后面的文件内容。

2.轮询

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];

IAsyncResult ar 
= fs.BeginRead(data, 0, data.Length, nullnull);
while (!ar.IsCompleted) {
Console.WriteLine(
"Operation not completed; still waiting.");
Thread.Sleep(
10);
}

Int32 bytesRead 
= fs.EndRead(ar);
fs.Close();
Console.WriteLine(
"Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(data, 
0, bytesRead));
}

我们可以看出轮询比较容易实现,但是效率可不高,因为我们要在花费一个线程来询问那个异步请求是否完成了,这样就浪费了一个线程。建议不要使用轮询。

下面我们看一下IAsyncResult的内容,前面已经用到了一个IsCompleted方法,后面可能还会用到它:

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

下面看一下轮询的另一种写法,效果和上面的一样:

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];
IAsyncResult ar 
= fs.BeginRead(data, 0, data.Length, nullnull);
while (!ar.AsyncWaitHandle.WaitOne(10false)) {
Console.WriteLine(
"Operation not completed; still waiting.");
}

Int32 bytesRead 
= fs.EndRead(ar);
fs.Close();
Console.WriteLine(
"Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(data, 
0, bytesRead));
}

3.方法回调,这个是这几种里面效率最高的

下面先看一个委托,因为所有的回调方法必须满足为这个委托的要求:

delegate void AsyncCallback(IAsyncResult ar);

using  System;
using  System.IO;
using  System.Threading;
public   static   class  Program  {
private static Byte[] s_data = new Byte[100];
public static void Main() {

Console.WriteLine(
"Main thread ID={0}",
Thread.CurrentThread.ManagedThreadId);

FileStream fs 
= new FileStream(@"C:Boot.ini", FileMode.Open,FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous);
fs.BeginRead(s_data, 
0, s_data.Length, ReadIsDone, fs);
Console.ReadLine() ;
}


private static void ReadIsDone(IAsyncResult ar) {
Console.WriteLine(
"ReadIsDone thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
FileStream fs 
= (FileStream) ar.AsyncState;
Int32 bytesRead 
= fs.EndRead(ar);
fs.Close();
Console.WriteLine(
"Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(s_data, 
0, bytesRead));
}

}


// 输出
Main thread ID = 1
ReadIsDone thread ID
= 4
Number of bytes read
= 100
5B
- 62 - 6F - 6F - 74 - 20 - 6C - 6F - 61 - 64 - 65 - 72 - 5D - 0D - 0A - 74 - 69 - 6D - 65 - 6F -
75 - 74 - 3D - 33 - 30 - 0D - 0A - 64 - 65 - 66 - 61 - 75 - 6C - 74 - 3D - 6D - 75 - 6C - 74 - 69 -
28 - 30 - 29 - 64 - 69 - 73 - 6B - 28 - 30 - 29 - 72 - 64 - 69 - 73 - 6B - 28 - 30 - 29 - 70 - 61 -
72 - 74 - 69 - 74 - 69 - 6F - 6E - 28 - 31 - 29 - 5C - 57 - 49 - 4E - 44 - 4F - 57 - 53 - 0D - 0A -
5B
- 6F - 70 - 65 - 72 - 61 - 74 - 69 - 6E - 67 - 20 - 73 - 79 - 73 - 74 - 65 - 6D - 73 - 5D - 0D

下面看一下如何利用c#的匿名方法更简单的实现:

public   static   void  Main()  {
Console.WriteLine(
"Main thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
FileStream fs 
= new FileStream(@"C:Boot.ini", FileMode.Open,FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous);
Byte[] data 
= new Byte[100];

fs.BeginRead(data, 
0, data.Length,

delegate(IAsyncResult ar)
{
Console.WriteLine(
"ReadIsDone thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
Int32 bytesRead 
= fs.EndRead(ar);
fs.Close();
Console.WriteLine(
"Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(data, 
0, bytesRead));
}
null);

Console.ReadLine();
}

下面看一下如何解决我们上面文件大小不同读取时间不一样的问题:

private   static   void  ReadMultipleFiles( params  String[] pathnames)  {
for (Int32 n = 0; n < pathnames.Length; n++{
Stream stream 
= new FileStream(pathnames[n], FileMode.Open,FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous);
new AsyncStreamRead(stream, 100,
delegate(Byte[] data)
{
Console.WriteLine(
"Number of bytes read={0}", data.Length);
Console.WriteLine(BitConverter.ToString(data));
}
);
}


Console.WriteLine(
"Hit <Enter> to end this program...");
Console.ReadLine() ;
}

private   delegate   void  StreamBytesRead(Byte[] streamData);
private   sealed   class  AsyncStreamRead  {
private Stream m_stream;
private Byte[] m_data;
StreamBytesRead m_callback;

public AsyncStreamRead(Stream stream, Int32 numBytes,StreamBytesRead callback) {
m_stream 
= stream;
m_data 
= new Byte[numBytes];
m_callback 
= callback;
stream.BeginRead(m_data, 
0, numBytes, ReadIsDone, null);
}


private void ReadIsDone(IAsyncResult ar) {
Int32 numBytesRead 
= m_stream.EndRead(ar);
m_stream.Close();
Array.Resize(
ref m_data, numBytesRead);
m_callback(m_data);
}

}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值