2015-05-27 71 views
2

我正在創建一個Windows窗體應用程序,我選擇一個包含多個* .txt文件的文件夾。它們的長度可能從幾千行(kB)到五千萬行(1GB)不等。代碼的每一行都有三個信息。日期在long中,int中的位置id和float中的值全部用分號(;)分隔。我需要計算所有這些文件中的最小值和最大值,並告訴它是哪個文件,然後是最常見的值。在C#中使用多線程處理多個文件的最佳方式是什麼?

我已經驗證了這些文件並存儲在了數組列表中。我正在打開一個線程來逐個讀取文件,並且我按行讀取數據。它工作正常,但是當有1GB文件時,我的內存不足。我嘗試將值存儲在字典中,其中鍵爲日期,值將是包含從文件名旁邊加載的所有信息的對象。我看到我無法使用字典,因爲在大約6M的值處,我用完了內存。所以我應該在多線程中做到這一點。我雖然可以運行兩個線程,一個讀取文件並將信息放入某種容器中,另一個線程讀取並執行計算,然後刪除容器中的值。但我不知道哪個容器可以做這樣的事情。此外,我需要計算最頻繁的值,因此需要將它們存儲在某處,這會將我引導回某種字典,但我已經知道我將耗盡內存。我對線程也沒有太多經驗,所以我不知道什麼是可能的。這是我到目前爲止的代碼:

GUI:

namespace STI { 
    public partial class GUI : Form { 
     private String path = null; 
     public static ArrayList txtFiles; 

     public GUI() { 
      InitializeComponent(); 
      _GUI1 = this; 
     } 

     //I run it in thread. I thought I would run the second 
     //one here that would work with the values inputed in some container 
     private void buttonRun_Click(object sender, EventArgs e) { 
      ThreadDataProcessing processing = new ThreadDataProcessing(); 
      Thread t_process = new Thread(processing.runProcessing); 
      t_process.Start(); 

      //ThreadDataCalculating calculating = new ThreadDataCalculating(); 
      //Thread t_calc = new Thread(calculating.runCalculation()); 
      //t_calc.Start(); 

     } 


    } 
} 

ThreadProcessing.cs

namespace STI.thread_package { 
    class ThreadDataProcessing { 
     public static Dictionary<long, object> finalMap = new Dictionary<long, object>(); 

     public void runProcessing() { 
      foreach (FileInfo file in GUI.txtFiles) { 
       using (FileStream fs = File.Open(file.FullName.ToString(), FileMode.Open)) 
       using (BufferedStream bs = new BufferedStream(fs)) 
       using (StreamReader sr = new StreamReader(bs)) { 
        String line; 
        String[] splitted; 
        try { 
         while ((line = sr.ReadLine()) != null) { 
          splitted = line.Split(';'); 

          if (splitted.Length == 3) { 
           long date = long.Parse(splitted[0]); 
           int location = int.Parse(splitted[1]); 
           float value = float.Parse(splitted[2], CultureInfo.InvariantCulture); 

           Entry entry = new Entry(date, location, value, file.Name); 

           if (!finalMap.ContainsKey(entry.getDate())) { 
            finalMap.Add(entry.getDate(), entry); 

           } 
          } 
         } 
         GUI._GUI1.update("File \"" + file.Name + "\" completed\n"); 
        } 
        catch (FormatException ex) { 
         GUI._GUI1.update("Wrong file format."); 
        } 
        catch (OutOfMemoryException) { 
         GUI._GUI1.update("Out of memory"); 
        } 
       } 

      } 
     } 
    } 
} 

,並在我把值在各條線上物體: Entry.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace STI.entities_package { 
    class Entry { 
     private long date; 
     private int location; 
     private float value; 
     private String fileName; 
     private int count; 

     public Entry(long date, int location, float value, String fileName) { 
      this.date = date; 
      this.location = location; 
      this.value = value; 
      this.fileName = fileName; 

      this.count = 1; 
     } 

     public long getDate() { 
      return date; 
     } 

     public int getLocation() { 
      return location; 
     } 

     public String getFileName() { 
      return fileName; 
     } 

    } 
} 
+2

TL; DR; [MCVE](http://stackoverflow.com/help/mcve) – Amit

+0

多線程當然不會幫你在這裏,因爲引入更多的線程並不會奇蹟般地爲你的進程增加更多的內存。編輯問題,以便刪除任何提及的多線程。也刪除GUI代碼,因爲這不是問題。 – Dialecticus

+0

現代計算機擁有大量內存,現在用完內存是一個相當愚蠢的問題。一個terabyte很容易來,也不花任何錢。 Project + Properties,Build選項卡,將平臺目標設置更改爲AnyCPU,在看到它時取消「首選32位」選項。你不喜歡它。 –

回答

4

我不認爲多線程可以幫助你 - 它可以幫助你分開IO來自CPU綁定任務的綁定任務,但是您的CPU綁定任務太微不足道了,我不認爲他們保證自己的線程。所有多線程將要做的事情是不必要地增加了問題的複雜性。

計算常量內存中的最小值/最大值很簡單:只保留噹噹前文件的值小於minFile或大於maxFile時更新的minFile和maxFile變量。查找最頻繁的值需要更多的內存,但只有幾百萬個文件,你應該有足夠的RAM來存儲維護每個值的頻率的Dictionary<float, int>,之後,通過迭代遍歷映射來確定哪個值具有最高頻率。如果由於某種原因,你沒有足夠的內存(如果內存不足,請確保你的文件正在關閉並進行垃圾回收,因爲具有數百萬個條目的Dictionary<float, int>應該適合少於1 GB的RAM ),那麼您可以對文件進行多次傳遞:在第一次傳遞時,將值存儲在Dictionary<interval, int>中,您將MIN_FLOAT和MAX_FLOAT之間的時間間隔分爲幾千個子區間,然後在下一次傳遞時可以忽略所有值不符合頻率最高的區間,從而縮小字典的大小。但是,Dictionary<float, int>應該適合內存,所以除非您開始處理數十億個文件而不是數百萬個文件,否則您可能不需要多遍處理。

+0

有趣,謝謝。我會試試看。你是對的,我可以將這些值存儲在字典中,因爲儘管最大的文件有大約45M行,但可能不會有那麼多的唯一值,所以字典可以適應限制,而不是將每行存儲在那裏。順便說一句。沒有那麼多文件。我有大約5-7個文件,它們只是大小不同。 – Arcane

+0

@Arcane Gotcha,我認爲這是每個值一個文件 –

相關問題