第一篇的內容請看這里: http://www.cnblogs.com/leizhang/archive/2012/04/13/2446220.html
代碼下載在文章最后
如何取消正在進行的異步操作
我們從前文可以知道我們進行的異步操作主要如下:
- 下載一個網頁
- 通過正則表達式分析網頁
我們可以發現步驟1是唯一耗時的操作,而且如果在我們點擊取消按鈕的時候我們如果已經下載了某一個網頁我們也沒必要中止第二步操作。
因此我們希望點擊按鈕的時候能夠中止所有正在下載網頁的異步操作.
這是我們之前的下載函數:
遺憾的是DownloadDataTaskAsync沒有提供支持異步取消的重載,因此我們需要自己來自定義一個,我們先查看DownloadDataTaskAsync本身的代碼:
// System.Net.WebClient
[ComVisible(false)]
[HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)]
public Task<byte[]> DownloadDataTaskAsync(Uri address)
{
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(address);
DownloadDataCompletedEventHandler handler = null;
handler = delegate(object sender, DownloadDataCompletedEventArgs e)
{
this.HandleCompletion<DownloadDataCompletedEventArgs, DownloadDataCompletedEventHandler, byte[]>(tcs, e, (DownloadDataCompletedEventArgs args) => args.Result, handler, delegate(WebClient webClient, DownloadDataCompletedEventHandler completion)
{
webClient.DownloadDataCompleted -= completion;
});
};
this.DownloadDataCompleted += handler;
try
{
this.DownloadDataAsync(address, tcs);
}
catch
{
this.DownloadDataCompleted -= handler;
throw;
}
return tcs.Task;
}
我們將使用擴展函數的方法為DownloadDataTaskAsync添加一個可以異步取消的重載:
public static Task<byte[]> DownloadDataTaskAsync(this WebClient webClient, Uri address, CancellationToken cancellationToken)
如果我們先不考慮擴展函數必須是靜態的話,我們對原函數進行改造:
// System.Net.WebClient
[ComVisible(false)]
[HostProtection(SecurityAction.LinkDemand, ExternalThreading = true)]
public Task<byte[]> DownloadDataTaskAsync(Uri address, CancellationToken cancellationToken)
{
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(address);
if (cancellationToken.IsCancellationRequested)
{
tcs.TrySetCanceled();
}
DownloadDataCompletedEventHandler handler = null;
handler = delegate(object sender, DownloadDataCompletedEventArgs e)
{
this.HandleCompletion<DownloadDataCompletedEventArgs, DownloadDataCompletedEventHandler, byte[]>(tcs, e, (DownloadDataCompletedEventArgs args) => args.Result, handler, delegate(WebClient webClient, DownloadDataCompletedEventHandler completion)
{
webClient.DownloadDataCompleted -= completion;
});
};
this.DownloadDataCompleted += handler;
try
{
this.DownloadDataAsync(address, tcs);
if (cancellationToken.IsCancellationRequested)
{
this.CancelAsync();
}
}
catch
{
this.DownloadDataCompleted -= handler;
throw;
}
return tcs.Task;
}
我們注意因為擴展函數必須是靜態的所以this.HandleCompletion顯然無法使用了,構造一個類解決這個問題:
internal static class EAPCommon
{
internal static void HandleCompletion<T>(TaskCompletionSource<T> tcs, bool requireMatch, AsyncCompletedEventArgs e, Func<T> getResult, Action unregisterHandler)
{
if (requireMatch)
{
if (e.UserState != tcs)
{
return;
}
}
try
{
unregisterHandler();
}
finally
{
if (e.Cancelled)
{
tcs.TrySetCanceled();
}
else
{
if (e.Error != null)
{
tcs.TrySetException(e.Error);
}
else
{
tcs.TrySetResult(getResult());
}
}
}
}
}
現在我們可以來構造我們的擴展函數:
internal static class AsyncExtensions
{
public static Task<byte[]> DownloadDataTaskAsync(this WebClient webClient, Uri address, CancellationToken cancellationToken)
{
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>(address);
if (cancellationToken.IsCancellationRequested)
{
tcs.TrySetCanceled();
}
else
{
CancellationTokenRegistration ctr = cancellationToken.Register(new Action(webClient.CancelAsync));
DownloadDataCompletedEventHandler completedHandler = null;
completedHandler = delegate(object sender, DownloadDataCompletedEventArgs e)
{
EAPCommon.HandleCompletion<byte[]>(tcs, true, e, () => e.Result, delegate
{
ctr.Dispose();
webClient.DownloadDataCompleted -= completedHandler;
});
};
webClient.DownloadDataCompleted += completedHandler;
try
{
webClient.DownloadDataAsync(address, tcs);
if (cancellationToken.IsCancellationRequested)
{
webClient.CancelAsync();
}
}
catch
{
webClient.DownloadDataCompleted -= completedHandler;
throw;
}
}
return tcs.Task;
}
}
好的現在我們有了可以異步取消的異步下載函數(真拗口),我們來改寫原來的程序:
新建如圖變量:
在獲取按鈕單擊事件中初始化它:
添加取消按鈕單擊事件,並添加如下代碼:
重寫下載函數:
修改用到下載函數的所有地方:
修改完畢。
注意我們新的下載函數會在調試時拋出異常,建議不要運行調試而是直接到文件夾運行程序:
點擊取消按鈕程序程序立即成功中止。
關於導出數據
數據導出非常簡單且方式多種多樣,所以這里就不浪費大家時間了,建議大家導出為csv文件這樣可以直接用Excel打開,而且csv是文本格式的逗號分割文件,不會有額外開銷,省時省力划算多了。
下面給出代碼下載,不包括數據導出: