[英]Out of Memory Exception When Using HttpWebRequest to Stream Large File
[英]Out of Memory Exception when using File Stream Write Byte to Output Progress Through the Console
我有以下代碼,在編寫大文件時會拋出內存不足異常。 有什么我想念的嗎?
我不確定為什么它會拋出內存不足錯誤,因為我認為Filestream僅將最大4096字節用於緩沖區? 老實說,我不完全了解Buffer的含義,不勝感激。
public static async Task CreateRandomFile(string pathway, int size, IProgress<int> prog)
{
byte[] fileSize = new byte[size];
new Random().NextBytes(fileSize);
await Task.Run(() =>
{
using (FileStream fs = File.Create(pathway,4096))
{
for (int i = 0; i < size; i++)
{
fs.WriteByte(fileSize[i]);
prog.Report(i);
}
}
}
);
}
public static void p_ProgressChanged(object sender, int e)
{
int pos = Console.CursorTop;
Console.WriteLine("Progress Copied: " + e);
Console.SetCursorPosition (0, pos);
}
public static void Main()
{
Console.WriteLine("Testing CopyLearning");
//CopyFile()
Progress<int> p = new Progress<int>();
p.ProgressChanged += p_ProgressChanged;
Task ta = CreateRandomFile(@"D:\Programming\Testing\RandomFile.asd", 99999999, p);
ta.Wait();
}
編輯:99,999,999剛創建為制作一個99MB的文件
注意:我已注釋掉prog.Report(i),它將正常工作。 似乎由於某種原因,該錯誤發生在該行
Console.writeline("Progress Copied: " + e);
我不完全確定為什么這會導致錯誤? 那么錯誤可能是由於progressEvent引起的嗎?
編輯2:我已遵循建議更改代碼,以便通過使用以下命令報告每4000字節的進度:
if (i%4000==0)
prog.Report(i);
由於某些原因。 我現在可以寫最大900MBs的文件。
我想問題是,為什么“ Edit 2”的代碼允許它寫多達900MB的大小? 是因為它報告進度並向控制台寫入的內容比以前減少了4000倍嗎? 我沒有意識到控制台會占用這么多的內存,尤其是因為我假設控制台所做的只是輸出“ Progress Copied”?
編輯3:
由於某些原因,當我將以下行更改為:
for (int i = 0; i < size; i++)
{
fs.WriteByte(fileSize[i]);
Console.Writeline(i)
prog.Report(i);
}
在prog.Report(i)之前有“ Console.Writeline()”的地方,它可以正常工作並復制文件,盡管這樣做需要很長時間。 這使我相信由於某種原因這是與控制台相關的問題,但是我不確定是什么。
fs.WriteByte(fileSize[i]);
prog.Report(i);
您制造了一個消防水帶問題 。 在死鎖和線程爭用之后,可能是線程引起的第三大最可能的問題。 同樣難以診斷。
使用調試器的“調試+ Windows +線程”窗口最容易看到,並查看正在執行CreateRandomFile()的線程。 幸運的是,您會看到它已完成並且已寫入所有99MB字節。 但是控制台上報告的進度遠遠落后於此,僅報告了125KB字節的寫入,傳遞或獲取。
核心問題是Progress <>。Report()的工作方式。 它使用SynchronizationContext.Post()來調用ProgressChanged事件處理程序。 在控制台模式的應用程序中,它將調用ThreadPool.QueueUserWorkItem()。 這相當快,它不會使您的CreateRandomFile()方法陷入困境。
但是事件處理程序本身要慢很多,控制台輸出不是很快。 因此,實際上,您正在以極大的速度添加線程池工作請求,這些請求在幾秒鍾內達到了9,900萬。 線程池調度程序無法跟上,您大約有4個線程同時執行。 所有人都爭相向控制台寫入數據,只有其中一個可以獲取基礎鎖。
因此,導致OOM的線程池調度程序被迫存儲了如此多的工作請求。
並且可以肯定的是,當您不經常調用Report()時,火災軟管問題就不會那么嚴重了。 盡管直接調用Console.Write()是一個明顯的解決方法,但實際上確保它永遠不會導致問題的方法並不那么簡單。 最終,簡單易用,創建對人類有用的可用UI。 沒有人喜歡瘋狂的滾動窗口或文本模糊。 每秒不超過20次的進度報告足以滿足用戶的需求,控制台也可以輕松地做到這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.