2015-04-03 125 views
0

我一直在試驗一些需要在地方重構的舊代碼,並測試是否有異步上傳文件(服務器端)的iis線程等有任何改進。使用jQuery文件上傳客戶端。使用異步&等待.net 4.5 mvc c#

原代碼

[HttpPost] 
public ActionResult UploadDocument(HttpPostedFileBase uploadedFile) { 

    // Do any validation here 

    // Read bytes from http input stream into fileData 
    Byte[] fileData; 

    using (BinaryReader binaryReader = 
      new BinaryReader(uploadedFile.InputStream)) { 

    fileData = binaryReader.ReadBytes(uploadedFile.ContentLength); 

    } 

    // Create a new Postgres bytea File Blob ** NOT Async ** 
    _fileService.CreateFile(fileData); 

    return Json(
    new { 
     ReturnStatus = "SUCCESS" // Or whatever 
    } 
); 

} 

新的代碼

[HttpPost] 
public async Task<ActionResult> UploadDocumentAsync(HttpPostedFileBase uploadedFile) { 

    // Do any validation here 

    // Read bytes from http input stream into fileData 
    Byte[] fileData = new Byte[uploadedFile.ContentLength]; 

    await uploadedFile.InputStream.ReadAsync(fileData, 0, uploadedFile.ContentLength); 

    // Create a new Postgres bytea File Blob ** NOT Async ** 
    _fileService.CreateFile(fileData); 

    return Json(
    new { 
     ReturnStatus = "SUCCESS" // Or whatever 
    } 
); 

} 

新方法看起來正常工作,但我的問題是:

是下面的代碼正確的(最好)的方法去做吧?有沒有這樣做的陷阱?那裏有很多矛盾和過時的信息。似乎還有很多關於實際做法是否有改進或有意義的爭論。是的,它回饋給iis等的線程,但它值得討論的開銷類型。

問題

// Read bytes from http input stream into fileData 
Byte[] fileData = new Byte[uploadedFile.ContentLength]; 

await uploadedFile.InputStream.ReadAsync(fileData, 0, uploadedFile.ContentLength); 
+2

async/await是我想的搖尾巴狗。希望看到一些證明我錯誤的論點。這是一個很好的問題。因此,它幾乎肯定會被一些過路衛隊管理員刪除。 – Sam 2015-04-03 14:32:28

+0

你爲什麼不使CreateFile異步?這是一個測試問題,看看你是否有特殊的常見誤解。同時,我想引用您對此主題的處理方式:http://stackoverflow.com/a/25087273/122718和http://stackoverflow.com/a/12796711/122718。 – usr 2015-04-03 14:52:21

+0

usr我可能會但那不是真的是我問。雖然關於這個話題,但是關於快速本地數據庫訪問是否更快地保持同步的爭論還有很多。 – 2015-04-03 14:56:19

回答

1

該代碼的代碼是正確的。使用異步IO不是全部或沒有。正如您所做的那樣,您可以安全地混合同步和異步IO。

Whether you should be using async has been covered already.對於ASP.NET而言,基本規則是在操作可能具有非常高的延遲並且被同時調用的情況下使用它。只有在這種情況下,不要釋放線程纔是重要的。如果操作很快或者很少,那麼沒有太多線程可以釋放,這是浪費開發人員的時間。

從緩衝(默認)輸入流讀取與從文件讀取相同。 ASP.NET將長輸入緩衝到磁盤上的文件。這是異步IO不能提供吞吐量增益的經典案例。在很快的情況下,文件可能被完全緩存,因此IO純粹是基於CPU的(來自緩存的memcpy)。開發時間和CPU週期的總體浪費。沒有任何好處。

+0

所以在這種情況下,你會說它是一個好東西,以異步文件上傳或不。 – 2015-04-03 15:45:59

+0

我的建議:由於您的併發上傳數量可能非常少,因此這個問題沒有實際意義。使用同步的默認值。如果你真的希望這是異步使得上傳無緩衝。這是一個ASP.NET設置的地方。 – usr 2015-04-03 15:48:49

+0

這是一個相當被誤解的主題和大量相互衝突的信息。它現在變得非常「時髦」,現在做異步的方式並沒有幫助。 – 2015-04-03 15:48:54

-2

爲了你正在付諸實施的異步麻煩等着你也許能夠做這樣的事情:

try 
{ 
    Task.Run(() => uploadedFile.InputStream.Read(fileData, 0, uploadedFile.ContentLength)); 
} 
catch() 
{ 
    // etc. 
} 

這會買你的並行性在你需要它(這異步不一定給你)和你可以花一點時間將一個像signalR這樣的websocket框架返回你的成功/失敗結果。

順便說一句你可以在該任務中放置更多的代碼..包括調用你的數據庫。我只是以此爲例。

編輯 .....根據我的回答,下面的評論是,在很多情況下,馬力可以指導和使用與任務與異步/等待更多prescision。這不是對異步等待的滿足,而只是使用正確的工具來完成工作。這是從生產服務器的一些代碼,不只是正是:

 Task t1 = Task.Run(() => GetBusinessProcesses(-1)); 
     Task t2 = Task.Run(() => GetActivityRoles(-1)); 
     Task t3 = Task.Run(() => Tools = toolService.GetTools().ToSelectListItem(x => x.Name, x => x.ToolId.ToString(), true).ToList()); 
     Task t4 = Task.Run(() => BusinessAreas = businessAreaService.GetBusinessAreas().ToSelectListItem(x => x.Name, x => x.BusinessAreaId.ToString(), true).ToList()) 
      .ContinueWith(t => 
     { 
      // Populate the functional area dropdown data 
      if (BusinessAreas != null && BusinessAreas.Any()) 
      { 
       int businessAreaId = System.Convert.ToInt32(BusinessAreas[0].Value); 
       GetFunctionalAreas(businessAreaId); 
      } 
     }); 

     try 
     { 
      Task.WaitAll(t1, t2, t3, t4); 
     } 
     catch (Exception ex) 
     { 
      throw new Exception("Error populating dropdowns. See inner exception.", ex); 
     } 

在上面的代碼我要對我的分貝四個平行的電話和並行建立四個列表。我等待任務並返回頁面。在OP的情況下,他可以這樣做:

Task t1 = Task.Run(() => parse the file).ContinueWith(x => write to the db); 
    Task t2 = Task.Run(() => more work); 
    // do some work here 
    Task.WaitAll(t1, t2); 
    // Return the page 
+0

這似乎有點矯枉過正,我認爲(儘管我可能錯了),爲什麼MS創造了新的方式來做到這一點。如果上傳沒有「阻止」直到它完成,那麼我的代碼實際上的應用程序將無法正常工作。有爭議的真正收益是從IIS發佈服務頁面的線程,而不是服務於長期持久的文件上傳。它並不真正讓它真正並行運行。如果真的如此,我需要這樣的並行性,我會考慮一個erlang後端而不是.net,儘管這是另一個辯論。 – 2015-04-03 17:19:45

+2

這使得操作失敗並忘記了這可能是不受歡迎的。此外,這是因爲這將在請求結束後使用請求對象。它也同時使用它們是活潑的。 – usr 2015-04-03 17:24:16

+0

畢竟你必須跳過來使用異步等待,我仍然想知道我發佈的代碼可能被稱爲矯枉過正。無論如何,我的答案是,異步/等待通常不會將您的disposl中的馬力指向最需要它的位置。請注意,異步/等待不是替代或替代並行。它不是新的方式來做到這一點。此外,IIS不知道或不在乎是否運行後臺任務。你的頁面也一樣。如果這不適合你的應用程序,那麼它就不適合你。我希望至少我已經提供了一些選擇讓你去探索。問候, – Sam 2015-04-03 17:36:38

1

具體回答你的問題:

  1. 有什麼不對您的代碼,而在我看來,你應該與該改變前進。根據各種因素(指出的文件大小/緩衝,併發等),它可能會或可能不會有顯着的改進(並且任何改進都將在可擴展性上,而不是速度上),但幾乎肯定不會是更差關閉。你所說的「開銷」涉及由編譯器編寫的狀態機,在大多數情況下它是nearly negligable

  2. 一個次要的疑難雜症,我能想到的:你會想看看發生了什麼await後發生並確保沒有假設,該代碼將在同一個線程中的代碼在await前運行。在這種情況下不太可能,但需要注意的事情。

我不同意async是服務器上幾乎沒有什麼好用的「時尚」的概念。這是對硬件趨勢的直接回應。處理器速度並沒有像以前那樣提高。今天,競賽需要更多內核,我們看到編程語言和框架對這一趨勢做出了反應,其功能使得編寫軟件更容易利用硬件級別的併發性。

相對長時間運行的I/O在服務器應用程序(處理文件,調用外部API等)中很常見,並且在這些操作期間釋放線程以完成其他工作幾乎總是有益的。

+0

是的,我同意另一個很好的答案。 – 2015-04-03 20:57:49