1.何为异步编程
异步编程就是把耗时的操作放入一个单独的线程中处理,不要阻塞主线程,线程任务完成后
通知主线程。
2.同步的方法实现(ui会卡住)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
textBox1.Text = "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe";
}
private void button_down_Click(object sender, EventArgs e)
{
textBox2.Text = "下载中...";
if (textBox1.Text == string.Empty)
{
MessageBox.Show("请先输入地址!");
return;
}
DownLoadFile(textBox1.Text.Trim());
}
public void DownLoadFile(string url)
{
int buffer_size = 2048;
byte[] buffer_read = new byte[buffer_size];
string save_path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\dotnet.exe";
FileStream file_stream = null;
HttpWebResponse my_web_response = null;
if(File.Exists(save_path))
{
File.Delete(save_path);
}
file_stream = new FileStream(save_path, FileMode.OpenOrCreate);
try
{
HttpWebRequest my_http_web_requset = (HttpWebRequest)WebRequest.Create(url);
if(my_http_web_requset != null)
{
my_web_response = (HttpWebResponse)my_http_web_requset.GetResponse();
Stream response_stream = my_web_response.GetResponseStream();
int read_size = response_stream.Read(buffer_read, 0, buffer_size);
while (read_size >0)
{
file_stream.Write(buffer_read, 0, read_size);
read_size = response_stream.Read(buffer_read, 0, buffer_size);
}
}
textBox2.Text = "文件下载完成,文件大小为:" + file_stream.Length +
"下载的路径为:" + save_path;
}
catch(Exception e)
{
textBox2.Text = "下载中发生异常 异常信息为:" + e.Message;
}
finally
{
if(my_web_response != null)
{
my_web_response.Close();
}
if(file_stream != null)
{
file_stream.Close();
}
}
}
}
3.利用APM方法同步(Asynchronous Programming Mode)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
textBox1.Text = "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe";
}
private delegate string AsyncMethodCaller(string fileurl);
SynchronizationContext sc;
private void button_down_Click(object sender, EventArgs e)
{
textBox2.Text = "下载中...";
button_down.Enabled = false;
if (textBox1.Text == string.Empty)
{
MessageBox.Show("请先输入地址!");
return;
}
sc = SynchronizationContext.Current;//获得调用线程同步的上下文对象
AsyncMethodCaller method_caller = new AsyncMethodCaller(DownLoadFile);
method_caller.BeginInvoke(textBox1.Text.Trim(),GetResult, null);
}
private void GetResult(IAsyncResult result) //异步完成后调用
{
AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate;
string return_string = caller.EndInvoke(result);
sc.Post(ShowState, return_string);
}
private void ShowState(object result)
{
textBox2.Text = result.ToString();
button_down.Enabled = true;
}
public string DownLoadFile(string url)
{
int buffer_size = 2048;
byte[] buffer_read = new byte[buffer_size];
string save_path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\dotnet.exe";
FileStream file_stream = null;
HttpWebResponse my_web_response = null;
if(File.Exists(save_path))
{
File.Delete(save_path);
}
file_stream = new FileStream(save_path, FileMode.OpenOrCreate);
try
{
HttpWebRequest my_http_web_requset = (HttpWebRequest)WebRequest.Create(url);
if(my_http_web_requset != null)
{
my_web_response = (HttpWebResponse)my_http_web_requset.GetResponse();
Stream response_stream = my_web_response.GetResponseStream();
int read_size = response_stream.Read(buffer_read, 0, buffer_size);
while (read_size >0)
{
file_stream.Write(buffer_read, 0, read_size);
read_size = response_stream.Read(buffer_read, 0, buffer_size);
}
}
return string.Format("文件下载完成,文件大小为: {0}下载的路径为:{1}" + file_stream.Length, save_path);
}
catch(Exception e)
{
return string.Format( "下载中发生异常 异常信息为:{0}" ,e.Message);
}
finally
{
if(my_web_response != null)
{
my_web_response.Close();
}
if(file_stream != null)
{
file_stream.Close();
}
}
}
}
4.EAP Event-based Asynchronous pattern)
基于事件的异步模式
public partial class Form1 : Form
{
private int download_size = 0;
public string download_path = null;
long total_size = 0;
const int buffer_size = 2048;
byte[] buffer_read = new byte[buffer_size];
FileStream file_stream = null;
HttpWebResponse my_web_response = null;
public Form1()
{
InitializeComponent();
textBox1.Text = "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe";
this.button_pause.Enabled = false;
GetTotalSize();
download_path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\"+Path.GetFileName(this.textBox1.Text.Trim());
if(File.Exists(download_path))
{
FileInfo file_info = new FileInfo(download_path);
download_size = (int)file_info.Length;
progressBar1.Value = (int)((float)download_size / (float)total_size * 100);
}
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
}
private void GetTotalSize()
{
HttpWebRequest my_http_web_request = (HttpWebRequest)WebRequest.Create(textBox1.Text.Trim());
HttpWebResponse response = (HttpWebResponse)my_http_web_request.GetResponse();
total_size = response.ContentLength;
response.Close();
}
private void button_down_Click(object sender, EventArgs e)
{
if(backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
file_stream = new FileStream(download_path, FileMode.OpenOrCreate);
file_stream.Seek(download_size, SeekOrigin.Begin);
this.button_down.Enabled = false;
this.button_pause.Enabled = true;
}
else
{
MessageBox.Show("正在执行操作,稍后");
}
}
private void button_pause_Click(object sender, EventArgs e)
{
if(this.backgroundWorker1.IsBusy &&
this.backgroundWorker1.WorkerSupportsCancellation == true)
{
backgroundWorker1.CancelAsync(); //取消下载
}
}
private void bgWorkFileDownLoad_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bg_worker = sender as BackgroundWorker;
try
{
HttpWebRequest my_http_web_requset = (HttpWebRequest)WebRequest.Create(textBox1.Text.Trim());
if(download_size != 0)
{
my_http_web_requset.AddRange(download_size);
}
my_web_response = (HttpWebResponse)my_http_web_requset.GetResponse();
Stream response_stream = my_web_response.GetResponseStream();
int read_size = 0;
while(true)
{
if(bg_worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
read_size = response_stream.Read(buffer_read, 0, buffer_read.Length);
if(read_size >0)
{
download_size += read_size;
int precent_complete = (int)((float)download_size / (float)total_size * 100);
file_stream.Write(buffer_read, 0, read_size);
bg_worker.ReportProgress(precent_complete);
}
else
{
break;
}
}
}
catch
{
throw;
}
}
private void bgWorkFileDownLoad_ProgressChange(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
private void bgWorkerFileDownLoad_RunComlete(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
MessageBox.Show(e.Error.Message);
my_web_response.Close();
}
else if(e.Cancelled)
{
MessageBox.Show(string.Format("下载暂停,下载文件的地址为{0}\n 已经下载的字节数为:{1}字节",download_path,download_size));
my_web_response.Close();
file_stream.Close();
this.button_down.Enabled = true;
this.button_pause.Enabled = false;
}
else
{
MessageBox.Show(string.Format("下载完成,下载文件的地址为{0}\n 已经下载的字节数为:{1}字节", download_path, total_size));
my_web_response.Close();
file_stream.Close();
this.button_down.Enabled = true;
this.button_pause.Enabled = false;
}
}
}
不过以上的方式并不推荐使用
5.TAP 基于任务的异步模式 (Task-based Asynchronous Pattern)
还有更厉害的
public partial class Form1 : Form
{
private int download_size = 0;
public string download_path = null;
long total_size = 0;
FileStream file_stream = null;
CancellationTokenSource cts = null;
Task task = null;
SynchronizationContext sc;
public Form1()
{
InitializeComponent();
textBox1.Text = "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe";
this.button_pause.Enabled = false;
GetTotalSize();
download_path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.textBox1.Text.Trim());
if (File.Exists(download_path))
{
FileInfo file_info = new FileInfo(download_path);
download_size = (int)file_info.Length;
progressBar1.Value = (int)((float)download_size / (float)total_size * 100);
}
}
private void GetTotalSize()
{
HttpWebRequest my_http_web_request = (HttpWebRequest)WebRequest.Create(textBox1.Text.Trim());
HttpWebResponse response = (HttpWebResponse)my_http_web_request.GetResponse();
total_size = response.ContentLength;
response.Close();
}
private void button_down_Click(object sender, EventArgs e)
{
file_stream = new FileStream(download_path, FileMode.OpenOrCreate);
file_stream.Seek(download_size, SeekOrigin.Begin);
this.button_down.Enabled = false;
this.button_pause.Enabled = true;
// 上下文同步
sc = SynchronizationContext.Current;
cts = new CancellationTokenSource();
task = new Task(() => Tap_DoWork(textBox1.Text.Trim(), cts.Token, new Progress<int>(p =>
{
sc.Post(new SendOrPostCallback((result) => progressBar1.Value = (int)result), p);
})));
task.Start();
}
private void button_pause_Click(object sender, EventArgs e)
{
cts.Cancel();
}
private void Tap_DoWork(string url, CancellationToken ct, IProgress<int> progess)
{
int buffer_size = 2048;
byte[] buffer_read = new byte[buffer_size];
HttpWebResponse my_web_response = null;
HttpWebRequest my_http_web_requset = null;
try
{
my_http_web_requset = (HttpWebRequest)WebRequest.Create(url);
if (download_size != 0)
{
my_http_web_requset.AddRange(download_size);
}
my_web_response = (HttpWebResponse)my_http_web_requset.GetResponse();
Stream response_stream = my_web_response.GetResponseStream();
int read_size = 0;
while (true)
{
if (ct.IsCancellationRequested == true)
{
MessageBox.Show(string.Format("下载暂停,下载文件的地址为{0}\n 已经下载的字节数为:{1}字节", download_path, download_size));
my_web_response.Close();
file_stream.Close();
sc.Post((state) =>
{
this.button_down.Enabled = true;
this.button_pause.Enabled = false;
}, null);
break;
}
read_size = response_stream.Read(buffer_read, 0, buffer_read.Length);
if (read_size > 0)
{
download_size += read_size;
int precent_complete = (int)((float)download_size / (float)total_size * 100);
file_stream.Write(buffer_read, 0, read_size);
progess.Report(precent_complete);
}
else
{
MessageBox.Show(string.Format("下载完成,下载文件的地址为{0}\n 已经下载的字节数为:{1}字节", download_path, total_size));
sc.Post((state) =>
{
this.button_down.Enabled = true;
this.button_pause.Enabled = false;
}, null);
my_web_response.Close();
file_stream.Close();
break;
}
}
}
catch (AggregateException ex)
{
ex.Handle(e => e is OperationCanceledException);
}
}
}
6.async await
public partial class Form1 : Form
{
private int download_size = 0;
public string download_path = null;
long total_size = 0;
FileStream file_stream = null;
CancellationTokenSource cts = null;
Task task = null;
public Form1()
{
InitializeComponent();
textBox1.Text = "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe";
this.button_pause.Enabled = false;
GetTotalSize();
download_path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.textBox1.Text.Trim());
if (File.Exists(download_path))
{
FileInfo file_info = new FileInfo(download_path);
download_size = (int)file_info.Length;
progressBar1.Value = (int)((float)download_size / (float)total_size * 100);
}
}
private void GetTotalSize()
{
HttpWebRequest my_http_web_request = (HttpWebRequest)WebRequest.Create(textBox1.Text.Trim());
HttpWebResponse response = (HttpWebResponse)my_http_web_request.GetResponse();
total_size = response.ContentLength;
response.Close();
}
private async void button_down_Click(object sender, EventArgs e)
{
file_stream = new FileStream(download_path, FileMode.OpenOrCreate);
file_stream.Seek(download_size, SeekOrigin.Begin);
this.button_down.Enabled = false;
this.button_pause.Enabled = true;
// 上下文同步
cts = new CancellationTokenSource();
await Tap_DoWork(textBox1.Text.Trim(), cts.Token, new Progress<int>(p => progressBar1.Value = p));
}
private void button_pause_Click(object sender, EventArgs e)
{
cts.Cancel();
}
private async Task Tap_DoWork(string url, CancellationToken ct, IProgress<int> progess)
{
int buffer_size = 2048;
byte[] buffer_read = new byte[buffer_size];
HttpWebResponse my_web_response = null;
HttpWebRequest my_http_web_requset = null;
try
{
my_http_web_requset = (HttpWebRequest)WebRequest.Create(url);
if (download_size != 0)
{
my_http_web_requset.AddRange(download_size);
}
my_web_response = (HttpWebResponse)my_http_web_requset.GetResponse();
Stream response_stream = my_web_response.GetResponseStream();
int read_size = 0;
while (true)
{
if (ct.IsCancellationRequested == true)
{
MessageBox.Show(string.Format("下载暂停,下载文件的地址为{0}\n 已经下载的字节数为:{1}字节", download_path, download_size));
my_web_response.Close();
file_stream.Close();
this.button_down.Enabled = true;
this.button_pause.Enabled = false;
break;
}
read_size =await response_stream.ReadAsync(buffer_read, 0, buffer_read.Length);
if (read_size > 0)
{
download_size += read_size;
int precent_complete = (int)((float)download_size / (float)total_size * 100);
await file_stream.WriteAsync(buffer_read, 0, read_size);
progess.Report(precent_complete);
}
else
{
MessageBox.Show(string.Format("下载完成,下载文件的地址为{0}\n 已经下载的字节数为:{1}字节", download_path, total_size));
this.button_down.Enabled = true;
this.button_pause.Enabled = false;
my_web_response.Close();
file_stream.Close();
break;
}
}
}
catch (AggregateException ex)
{
ex.Handle(e => e is OperationCanceledException);
}
}
}
所有的一切都封装在async和 await 关键字中
在方法的头部多了 async关键字在具体的方法处增加 awit关键字。
也不存在获取上下问同步对象的代码 async 和 await关键字不会让调用的
方法运行在新的线程中,而是将方法分割成多个片段(片段的界限出现在方法内使用
await的关键字出)并使其中一些片段可以异步运行。await关键字出的代码片段是在线程上
运行的,而整个帆帆的调用确实同步。这种方式的编程不用考虑跨线程访问ui控件的问题。
而大大降低了异步编程的出错概率。