2013-06-05 103 views
1

有一個應用程序可以根據某些條件對大的txt文件進行排序。 我需要開始例如5個線程,但我從文件與foreach循環逐行閱讀。 如果我用我的代碼啓動5個線程,所有線程將採用相同的行。如何使用foreach循環制作多線程應用程序

這裏是我的代碼,我開始1線:

Thread[] thr; 
    private void button1_Click(object sender, EventArgs e) 
    { 
     button1.Enabled = false; 
     button4.Enabled = true; 
     decimal value = 1; 
     int i = 0; 
     int j = (int)(value); 
     thr = new Thread[j]; 
     for (; i < j; i++) 
     { 
      thr[i] = new Thread(new ThreadStart(go)); 
      thr[i].IsBackground = true; 
      thr[i].Start(); 
     } 
    } 

    private static IEnumerable<string> ReadLineFromFile(TextReader fileReader) 
    { 
     using (fileReader) 
     { 
      string currentLine; 
      while ((currentLine = fileReader.ReadLine()) != null) 
      { 
       yield return currentLine; 
      } 
     } 
    } 


    public void go() 
    { 
     while (true) 
     { 
      TextReader readFile = new StreamReader(file_path, System.Text.Encoding.UTF8, true); 
      foreach (string line in ReadLineFromFile(readFile)) 
      { 
       if (line.Split(':')[0].Contains("@")) 
       { 
        string out_line = line.Split(':')[0].Replace("+", "") + ":" + line.Split(':')[1]; 
        lock (locker) 
        { 
         mail_count++; 
         log_mail(mail_count); 
         mail.Add(out_line.Trim().Replace(";", ":")); 
        } 
       } 
       else 
       { 
        string out_line = line.Split(':')[0].Replace("+", "") + ":" + line.Split(':')[1]; 
        lock (locker) 
        { 
         rubbish_count++; 
         log_rubbish(rubbish_count); 
         rubbish.Add(out_line.Trim()); 
        } 
       } 
      } 
      MessageBox.Show("Ready"); 
      BeginInvoke(
      new MethodInvoker(() => 
      { 
       button1.Enabled = true; 
       button4.Enabled = false; 
      })); 
      break; 
     } 
    } 
+2

老實說,不管你做什麼,你的硬盤只能是在給定時間一個位置,所以沒有真正的點「並行「一個文件讀取。 然而,你可以給每個線程提供一個偏移量,以便從文件中開始讀取,但這不會加速任何事情^^ – C4stor

+0

如果你真的想加速你的函數,你必須使用磁盤緩存來獲得你的優勢:在實際需要之前開始閱讀文件。這會自動將文件加載到內存中(http://en.wikipedia.org/wiki/Page_cache),然後在實際需要時可以從RAM中讀取它。然後,您可以充分利用Pako生產者 - 消費者模式。 – C4stor

回答

3

爲什麼不平常的生產者 - 消費者模式走向何方?使一個線程讀取文件,將行放入某個共享集合中,而其他線程只是從集合中選取數據並對其進行處理。

更多 - 你可以從文件中讀取,併爲每一行創建Task,這將處理這一行,並把結果放在輸出集合中。

這看起來好於5線程試圖讀取同一個文件,而不是多次讀取同一行。

+0

這很好,但是讀取文件的速度可能比他對文件處理的速度慢幾個數量級,所以這可能沒有意義。 – C4stor

+0

因爲我加載了數百萬行txt文件,如果我喜歡你說的話,我會得到內存異常 – obdgy

+0

我完全同意你的看法。我只想給出一個關於如何解決一般相似問題的提示。在這種特殊情況下,這種解決方案不會帶來預期的收益 - 這是真的 – Pako

4

所有線程都讀取同一文件並從共享文件中讀取是困難和低效的。

在你的主要功能,你需要這樣的:

Parallel.ForEach(System.IO.File.ReadLines(file_path, System.Text.Encoding.UTF8), 
    line => ProcessOneLine(line) 
); 

然後ProcessOneLine會做.Split(':')

+0

ProcessOneLine是什麼? – obdgy

+0

您將必須編寫'void ProcessOneLine(string line){...}'。做一行所需的一切。線程由'Parallel'類提供。 –

+0

@HenkHolterman通過使用Parallel.ForEach,它將讀取整個文件(這裏有數百萬行)。使用BlockingCollection 來實現生產者 - 消費者模式是IMO的替代方式。 – ValidfroM

0

我想確認並擴大在什麼PAKO說。其他線程應使用來自包含數據的共享集合中的數據並對其進行處理。

讓多個線程訪問文本文件聽起來像是發生競態條件的可能性。如果一個線程正在更改一個文件,而另一個線程正在從中讀取文件,則可能會出現基本上不可預知的結果。

當我使用多線程訪問同一個文本文件並且可以推薦使用它時,我也遇到過BSOD。但是,如果您堅持這樣做,我建議您查看「鎖定」關鍵字和單例設計模式。這將允許您確保一次只有一個線程正在訪問該文件。

相關鏈接:

http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.80).aspx http://en.wikipedia.org/wiki/Singleton_pattern

http://en.wikipedia.org/wiki/Double-checked_locking