C#异步编程

同步方法和异步方法的区别
同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果
异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作

异步编程概览
.NET Framework 允许您异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名

的 BeginInvoke 和 EndInvoke 方法。

BeginInvoke 方法用于启动异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)。

BeginInvoke 立即返回,不等待异步调用完成。
BeginInvoke 返回 IasyncResult,可用于监视调用进度。

EndInvoke 方法用于检索异步调用结果。调用 BeginInvoke 后可随时调用 EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到

异步调用完成。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由

BeginInvoke 返回的 IAsyncResult。

四种使用 BeginInvoke 和 EndInvoke 进行异步调用的常用方法。调用了 BeginInvoke 后,可以:

1.进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
2.使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用

EndInvoke。这里主要是主程序等待异步方法,等待异步方法的结果。
3.轮询由 BeginInvoke 返回的 IAsyncResult,IAsyncResult.IsCompeted确定异步调用何时完成,然后调用 EndInvoke。此处理个人认为与
相同。
4.将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。这是在强制装

换回调函数里面IAsyncResult.AsyncState(BeginInvoke方法的最后一个参数)成委托,然后用委托执行EndInvoke。
警告   始终在异步调用完成后调用 EndInvoke。

以上有不理解的稍后可以再理解。

 

例子
1)先来个简单的没有回调函数的异步方法例子

请再运行程序的时候,仔细看注释,对理解很有帮助。还有,若将注释的中的两个方法都同步,你会发现异步运行的速度优越性。

 

using  System;

namespace  ConsoleApplication1
{
     
class Class1
     
{
         
//声明委托
         public delegate void AsyncEventHandler();

         
//异步方法
         void Event1()
        
{
            Console.WriteLine(
"Event1 Start");
            System.Threading.Thread.Sleep(
4000);
            Console.WriteLine(
"Event1 End");
        }


        
// 同步方法
        void Event2()
        
{
            Console.WriteLine(
"Event2 Start");
            
int i=1;
            
while(i<1000)
            
{
                i
=i+1;
                Console.WriteLine(
"Event2 "+i.ToString());
            }

            Console.WriteLine(
"Event2 End");
        }


        [STAThread]
        
static void Main(string[] args)
        
{
            
long start=0;
            
long end=0;
            Class1 c 
= new Class1();
            Console.WriteLine(
"ready");
            start
=DateTime.Now.Ticks;

            
//实例委托
            AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
            
//异步调用开始,没有回调函数和AsyncState,都为null
            IAsyncResult ia = asy.BeginInvoke(nullnull);
            
//同步开始,
            c.Event2();
            
//异步结束,若没有结束,一直阻塞到调用完成,在此返回该函数的return,若有返回值。

           
            asy.EndInvoke(ia);

            
//都同步的情况。
            
//c.Event1();
            
//c.Event2();
           
            end 
=DateTime.Now.Ticks;
            Console.WriteLine(
"时间刻度差="+ Convert.ToString(end-start) );
            Console.ReadLine();
        }

    }

}

 

2)下面看有回调函数的WebRequest和WebResponse的异步操作。

using  System;
using  System.Net;
using  System.Threading;
using  System.Text;
using  System.IO;


//  RequestState 类用于通过
//  异步调用传递数据
public   class  RequestState
{
    
const int BUFFER_SIZE = 1024;
    
public StringBuilder RequestData;
    
public byte[] BufferRead;
    
public HttpWebRequest Request;
    
public Stream ResponseStream;
    
// 创建适当编码类型的解码器
    public Decoder StreamDecode = Encoding.UTF8.GetDecoder();

    
public RequestState()
    
{
        BufferRead 
= new byte[BUFFER_SIZE];
        RequestData 
= new StringBuilder("");
        Request 
= null;
        ResponseStream 
= null;
    }

}


//  ClientGetAsync 发出异步请求
class  ClientGetAsync
{
    
public static ManualResetEvent allDone = new ManualResetEvent(false);
    
const int BUFFER_SIZE = 1024;

    
public static void Main(string[] args)
    
{

        
if (args.Length < 1)
        
{
            showusage();
            
return;
        }


        
// 从命令行获取 URI
        Uri HttpSite = new Uri(args[0]);

        
// 创建请求对象
        HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(HttpSite);

        
// 创建状态对象
        RequestState rs = new RequestState();

        
// 将请求添加到状态,以便它可以被来回传递
        rs.Request = wreq;

        
// 发出异步请求
        IAsyncResult r = (IAsyncResult)wreq.BeginGetResponse(new AsyncCallback(RespCallback), rs);

        
// 将 ManualResetEvent 设置为 Wait,
        
// 以便在调用回调前,应用程序不退出
        allDone.WaitOne();
    }


    
public static void showusage()
    
{
        Console.WriteLine(
"尝试获取 (GET) 一个 URL");
        Console.WriteLine(
"\r\n用法::");
        Console.WriteLine(
"ClientGetAsync URL");
        Console.WriteLine(
"示例::");
        Console.WriteLine(
"ClientGetAsync http://www.microsoft.com/net/");
    }


    
private static void RespCallback(IAsyncResult ar)
    
{
        
// 从异步结果获取 RequestState 对象
        RequestState rs = (RequestState)ar.AsyncState;

        
// 从 RequestState 获取 HttpWebRequest
        HttpWebRequest req = rs.Request;

        
// 调用 EndGetResponse 生成 HttpWebResponse 对象
        
// 该对象来自上面发出的请求
        HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(ar);

        
// 既然我们拥有了响应,就该从
        
// 响应流开始读取数据了
        Stream ResponseStream = resp.GetResponseStream();

        
// 该读取操作也使用异步完成,所以我们
        
// 将要以 RequestState 存储流
        rs.ResponseStream = ResponseStream;

        
// 请注意,rs.BufferRead 被传入到 BeginRead。
        
// 这是数据将被读入的位置。
        IAsyncResult iarRead = ResponseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
    }



    
private static void ReadCallBack(IAsyncResult asyncResult)
    
{
        
// 从 asyncresult 获取 RequestState 对象
        RequestState rs = (RequestState)asyncResult.AsyncState;

        
// 取出在 RespCallback 中设置的 ResponseStream
        Stream responseStream = rs.ResponseStream;

        
// 此时 rs.BufferRead 中应该有一些数据。
        
// 读取操作将告诉我们那里是否有数据
        int read = responseStream.EndRead(asyncResult);

        
if (read > 0)
        
{
            
// 准备 Char 数组缓冲区,用于向 Unicode 转换
            Char[] charBuffer = new Char[BUFFER_SIZE];

            
// 将字节流转换为 Char 数组,然后转换为字符串
            
// len 显示多少字符被转换为 Unicode
            int len = rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0);
            String str 
= new String(charBuffer, 0, len);

            
// 将最近读取的数据追加到 RequestData stringbuilder 对象中,
            
// 该对象包含在 RequestState 中
            rs.RequestData.Append(str);


            
// 现在发出另一个异步调用,读取更多的数据
            
// 请注意,将不断调用此过程,直到
            
// responseStream.EndRead 返回 -1
            IAsyncResult ar = responseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
        }

        
else
        
{
            
if (rs.RequestData.Length > 1)
            
{
                
// 所有数据都已被读取,因此将其显示到控制台
                string strContent;
                strContent 
= rs.RequestData.ToString();
                Console.WriteLine(strContent);
            }


            
// 关闭响应流
            responseStream.Close();

            
// 设置 ManualResetEvent,以便主线程可以退出
            allDone.Set();
        }

        
return;
    }

}


 
在这里有回调函数,且异步回调中又有异步操作。

首先是异步获得ResponseStream,然后异步读取数据。

这个程序非常经典。从中可以学到很多东西的。我们来共同探讨。

 

总结
上面说过,.net framework 可以异步调用任何方法。所以异步用处广泛。

在.net framework 类库中也有很多异步调用的方法。一般都是已Begin开头End结尾构成一对,异步委托方法,外加两个回调函数和AsyncState参数,组成异步操作的宏观体现。所以要做异步编程,不要忘了委托delegate、Begin,End,AsyncCallBack委托,AsyncState实例(在回调函数中通过IAsyncResult.AsyncState来强制转换),IAsycResult(监控异步),就足以理解异步真谛了。

 

C#异步复制大文件收藏
新一篇: C#多线程参数传递

Using Directives Using Directives
#region Using Directives
using System;
using System.IO ;
using System.Xml ;
using System.Collections ;
using System.Reflection ;
using System.Text ;
using System.Data ;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;
using System.Threading ;
#endregion


namespace  WindowsApplication4
{
/**//**//**//// <summary>
/// Copy Large File
/// </summary>

public class frmMain : System.Windows.Forms.Form
{
#region
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button btnCopy;
/**//**//**//// <summary>
/// 必需的设计器变量。
/// </summary>

private System.ComponentModel.Container components = null;

public frmMain()
{
   
//
   
// Windows 窗体设计器支持所必需的
   
//
   InitializeComponent();

   
//
   
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
   
//
}


/**//**//**//// <summary>
/// 清理所有正在使用的资源。
/// </summary>

protected override void Dispose( bool disposing )
{
   
if( disposing )
   
{
    
if (components != null
    
{
     components.Dispose();
    }

   }

   
base.Dispose( disposing );
}


Initialize Components
Initialize Components#region Initialize Components
/**//**//**//// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>

private void InitializeComponent()
{
   
this.progressBar1 = new System.Windows.Forms.ProgressBar();
   
this.btnCopy = new System.Windows.Forms.Button();
   
this.SuspendLayout();
   
// 
   
// progressBar1
   
// 
   this.progressBar1.Location = new System.Drawing.Point(816);
   
this.progressBar1.Name = "progressBar1";
   
this.progressBar1.Size = new System.Drawing.Size(20816);
   
this.progressBar1.TabIndex = 0;
   
// 
   
// btnCopy
   
// 
   this.btnCopy.Location = new System.Drawing.Point(848);
   
this.btnCopy.Name = "btnCopy";
   
this.btnCopy.TabIndex = 1;
   
this.btnCopy.Text = "Copy";
   
this.btnCopy.Click += new System.EventHandler(this.btnCopy_Click);
   
// 
   
// frmMain
   
// 
   this.AutoScaleBaseSize = new System.Drawing.Size(513);
   
this.ClientSize = new System.Drawing.Size(23277);
   
this.Controls.Add(this.btnCopy);
   
this.Controls.Add(this.progressBar1);
   
this.Name = "frmMain";
   
this.Text = "Copy File";
   
this.ResumeLayout(false);

}

#endregion


/**//**//**//// <summary>
/// Entry Point
/// </summary>

[STAThread]
static void Main() 
{
   Application.Run(
new frmMain());
}



#endregion


int totalSize; //Total Size
int position;   //Position
const int BUFFER_SIZE = 4096
byte[] buffer;
Stream stream;

private void btnCopy_Click(object sender, System.EventArgs e)
{
   
string strFile = "";

   OpenFileDialog dlg 
= new OpenFileDialog();
   
if ( dlg.ShowDialog() == DialogResult.OK )
   
{
    strFile 
= dlg.FileName ;
   }

   
else
   
{
    
return ;
   }


   FileStream fs 
= new FileStream( strFile , FileMode.Open , FileAccess.Read ) ;
   totalSize 
= (int)fs.Length ;
   stream 
= fs;

   
//Delete file which aready exists.
   if ( File.Exists( "c:\copyedFile.bin" ) )
    File.Delete( 
"c:\copyedFile.bin" );

   
//Copy file while larger than 4KB.
   if ( totalSize > BUFFER_SIZE )
   
{
    buffer 
= new byte[ BUFFER_SIZE ];

    
// Async Invoke
    stream.BeginRead( buffer , 0 , BUFFER_SIZE , new AsyncCallback( AsyncCopyFile ) , null );   
   }

   
else
   
{
    fs.Close();
   }

   
}

        
/**//**//**//// <summary>
/// Asynchronously copy file
/// </summary>
/// <param name="ar"></param>

private void AsyncCopyFile( IAsyncResult ar )
{
   
int readedLength ;
   
   
// Lock FileStream
   lock( stream )
   
{
    readedLength 
= stream.EndRead( ar );   // When stream endread, get readed length
   }


   
// Write to disk
   FileStream fsWriter = new FileStream( "C:\copyedFile.bin" , FileMode.Append , FileAccess.Write );
   fsWriter.Write( buffer , 
0 , buffer.Length );
   fsWriter.Close();

   
// Current stream position 
   position += readedLength;
     
   
// Response UI
   MethodInvoker m = new MethodInvoker( SynchProgressBar );
   m.BeginInvoke( 
null , null );

   
if ( position >= totalSize ) // Read over.
   {
    stream.Close();        
//Close FileStream 
    return ;
   }


   
// Continue to read and write
   lock ( stream )
   
{
    
int leftSize = totalSize - position;

    
if ( leftSize < BUFFER_SIZE ) 
     buffer 
= new byte[ leftSize ];

    stream.BeginRead( buffer , 
0 , buffer.Length , new AsyncCallback( AsyncCopyFile ) , null );     
      
   }

}


private void SynchProgressBar()
{
   
this.progressBar1.Maximum = totalSize;
   
this.progressBar1.Value   = position ; 
}


}

}

转载于:https://www.cnblogs.com/Kevan/archive/2008/06/18/1224717.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值