2015-11-22 45 views
0

我有看起來像這樣的同步方法:TAP - 使用內部EAP-方法來減少代碼

public void DownloadPackages() 
    { 
     long received = 0; 
     double total = PackageConfigurations.Select(config => SizeHelper.GetRemoteFileSize(config.UpdatePackageUri)) 
      .Where(updatePackageSize => updatePackageSize != null) 
      .Sum(updatePackageSize => updatePackageSize.Value); 

     if (!Directory.Exists(_applicationUpdateDirectory)) 
      Directory.CreateDirectory(_applicationUpdateDirectory); 

     foreach (var updateConfiguration in PackageConfigurations) 
     { 
      WebResponse webResponse = null; 
      try 
      { 
       var webRequest = WebRequest.Create(updateConfiguration.UpdatePackageUri); 
       using (webResponse = webRequest.GetResponse()) 
       { 
        var buffer = new byte[1024]; 
        _packageFilePaths.Add(new UpdateVersion(updateConfiguration.LiteralVersion), 
         Path.Combine(_applicationUpdateDirectory, 
          $"{updateConfiguration.LiteralVersion}.zip")); 
        using (FileStream fileStream = File.Create(Path.Combine(_applicationUpdateDirectory, 
         $"{updateConfiguration.LiteralVersion}.zip"))) 
        { 
         using (Stream input = webResponse.GetResponseStream()) 
         { 
          if (input == null) 
           throw new Exception("The response stream couldn't be read."); 

          int size = input.Read(buffer, 0, buffer.Length); 
          while (size > 0) 
          { 
           if (_downloadCancellationTokenSource.IsCancellationRequested) 
           { 
            fileStream.Flush(); 
            fileStream.Close(); 
            throw new OperationCanceledException(_downloadCancellationTokenSource.Token); 
           } 

           fileStream.Write(buffer, 0, buffer.Length); 
           received += size; 
            OnUpdateDownloadProgressChanged(received, 
             (long)total, (float)(received/total) * 100); 
           size = input.Read(buffer, 0, buffer.Length); 
          } 

         } 
        } 
       } 
      } 
      finally 
      { 
       webResponse?.Close(); 
      } 
     } 
    } 

嗯,我已經實現了EAP。這意味着,我已經寫了包裝在一個任務此方法並引發事件的方法:

public void DownloadPackagesAsync() 
    { 
     _downloadCancellationTokenSource.Dispose(); 
     _downloadCancellationTokenSource = new CancellationTokenSource(); 
       Task.Factory.StartNew(DownloadPackages).ContinueWith(DownloadTaskCompleted, 
      _downloadCancellationTokenSource.Token, 
      TaskContinuationOptions.None, 
      TaskScheduler.Default); 
    } 

private void DownloadTaskCompleted(Task task) 
    { 
     if (task.IsCanceled) 
      return; 

     var exception = task.Exception; 
     if (exception != null) 
      OnUpdateDownloadFailed(exception.InnerException ?? exception); 
     else 
      OnUpdateDownloadFinished(this, EventArgs.Empty); 
    } 

第一個問題:是不是正確的同步方式更新進度或者是有另一種方式?

第二個問題:有,在我的班級管理CancellationTokenSource的私人領域,我提供,看起來像這樣的方法:

public void CancelDownload() 
    { 
     _downloadCancellationTokenSource.Cancel(); 
    } 

當正在調用該方法,在下載被取消。但是,當我使用TAP時,這也是正確的嗎?

現在,第三個問題涉及使用TAP的異步方法。 它應該是public async Task DownloadPackagesTaskAsync(IProgress<UpdateDownloadProgressChangedEventArgs> progress)。實際上,我需要複製整個代碼並對其進行調整,但是這會給我帶來很多冗餘線路,我想。我想過在內部使用EAP方法。問題是我沒有任何返回類型,並且連續不能使用TaskCompletionSource以便使用它。我的方向是在.NET框架中的代碼:http://referencesource.microsoft.com/#System/net/System/Net/webclient.cs,d250a06fb9c3ac77,references

有沒有什麼辦法可以實現這個目標?

+0

使用'StartNew'編寫假異步方法[是反模式](http://blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx)。如果你的工作是異步的,那麼API應該是異步的。如果您需要爲反向編譯原因支持同步API,請使用[在我的關於brownfield async的文章中描述的布爾標誌參數hack(https://msdn.microsoft.com/zh-cn/magazine/mt238404.aspx))。 –

回答

3

嗯,我已經實現了EAP

爲什麼?特別是因爲你顯然打算實施TAP,爲什麼要打擾EAP呢?對於這個問題,爲什麼DownloadPackages()是同步的? Web APIs非常好地異步工作,使用它們可以更有效地利用線程資源,當然,它自然適合您自己的代碼使用基於TAP的API。

就個人而言,我只想改變DownloadPackages()async Task DownloadPackagesAsync()(或者,如果你喜歡,以及包括IProgress<T>參數),使得該方法中的適當變化來調用Web與await異步文件API,並留在這一點。

那就說&hellip;

更新同步方法的進度還是有其他方法是正確的嗎?

根據什麼標準「正確」?

當然,它會工作,因爲你有它。你遺漏了OnUpdateDownloadProgressChanged()的實現,所以我們不可能知道具體做了什麼。但是EAP代碼的客戶需要根據需要自己處理跨線程調用的情況並不少見,所以我在這裏看不到任何錯誤。

當然,請注意,如果您將主要方法實施爲async,則會產生編組回到正確的進度更新上下文的效果。哪個更好。但不是必需的。

我提供了一個看起來像這樣的方法&hellip; 當這個方法被調用時,下載被取消。但是,當我使用TAP時,這也是正確的嗎?

再次,根據什麼標準「正確」?我個人的感覺是,它是。我不想公開TaskCancellationSource本身,所以用公開的方法封裝取消操作對我來說似乎是正確的。但很難說如果不知道你想要判斷設計的標準是什麼。

現在,第三個問題涉及使用TAP的異步方法。它應該是public async Task DownloadPackagesTaskAsync(IProgress<UpdateDownloadProgressChangedEventArgs> progress)。實際上,我需要複製整個代碼並對其進行調整,但是這會給我帶來很多冗餘線路,我想。我想過在內部使用EAP方法。問題是我沒有任何返回類型,並且連續不能使用TaskCompletionSource以便使用它。 &hellip;有什麼辦法可以做到這一點?

同樣,如果您只是將原始API實現爲async,那甚至不會出現。這就是說,我不明白你的意見。如果你想利用你的代碼現在目前作爲一個TAP風格的API,在我看來,你可以簡單地這樣做:

public Task DownloadPackagesAsync() 
{ 
    _downloadCancellationTokenSource.Dispose(); 
    _downloadCancellationTokenSource = new CancellationTokenSource(); 

    return Task.Factory.StartNew(DownloadPackages).ContinueWith(DownloadTaskCompleted, 
     _downloadCancellationTokenSource.Token, 
     TaskContinuationOptions.None, 
     TaskScheduler.Default); 
} 

添加的IProgress<T>是微不足道的。如果您真的想保留EAP和TAP風格的API,您只需根據自己的喜好相互適應。例如,如果您希望底層實現使用EAP,並將其封裝在TAP中,則可以親自訂閱相應的事件,並在您的處理程序中調用IProgress<T>.Report()。或者,如果您希望底層實現使用TAP,則可以在EAP包裝器中將IProgress<T>實例傳遞給實現,並在IProgress<T>的回調中引發相應的事件。

我不明白缺乏返回類型與什麼有關。沒有人強迫你沒有返回類型,是嗎?我也沒有看到需要使用TaskCompletionSource;您已經有多個選項可用於返回將在適當時間完成的對象Task

+0

感謝您的回覆。應該有可能使用EAP,因爲有些客戶希望保留.NET 4.0而不必安裝任何NuGet軟件包(Microsoft Async等)。 但我可以考慮一下。 那麼,我沒有指定任何標準,我只是想到了正常的TAP實現。 你的想法給了我解決方案,我想。我可以返回由Task.Factory.StartNew(...)創建的'Task'並更改返回類型。從這一點來看,在TAP方法內部應該很容易使用這種方法。 –