天天看點

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

第一篇的内容請看這裡: http://www.cnblogs.com/leizhang/archive/2012/04/13/2446220.html

代碼下載下傳在文章最後

如何取消正在進行的異步操作

我們從前文可以知道我們進行的異步操作主要如下:

  1. 下載下傳一個網頁
  2. 通過正規表達式分析網頁

我們可以發現步驟1是唯一耗時的操作,而且如果在我們點選取消按鈕的時候我們如果已經下載下傳了某一個網頁我們也沒必要中止第二步操作。

是以我們希望點選按鈕的時候能夠中止所有正在下載下傳網頁的異步操作.

這是我們之前的下載下傳函數:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

遺憾的是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;

  }

}

好的現在我們有了可以異步取消的異步下載下傳函數(真拗口),我們來改寫原來的程式:

建立如圖變量:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

在擷取按鈕單擊事件中初始化它:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

添加取消按鈕單擊事件,并添加如下代碼:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

重寫下載下傳函數:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

修改用到下載下傳函數的所有地方:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung
跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

修改完畢。

注意我們新的下載下傳函數會在調試時抛出異常,建議不要運作調試而是直接到檔案夾運作程式:

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

點選取消按鈕程式程式立即成功中止。

關于導出資料

資料導出非常簡單且方式多種多樣,是以這裡就不浪費大家時間了,建議大家導出為csv檔案這樣可以直接用Excel打開,而且csv是文本格式的逗号分割檔案,不會有額外開銷,省時省力劃算多了。

下面給出代碼下載下傳,不包括資料導出:

下載下傳代碼點這裡

跟我一起制作資料采集-擷取淘寶網店寶貝資料資訊(二) - Eckel Cheung

繼續閱讀