簡體   English   中英

使用文件流寫入字節通過控制台輸出進度時出現內存不足異常

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM