2013-01-14 41 views
-2

我必須處理大約170.000個文件,並且想要使用多個線程。 文件的名稱按照年份數格式連續,並按文件夾中的年份排序。 (但它們可以全部在同一個文件夾中)。 不同的年份有不同的文件數量。文件很小,只有幾個(每個文件大小爲< 20 KB)。在多個線程上打開多個文件

由於處理任務的輸出將存儲在SQL數據庫中,所以處理女巫的訂單是無關緊要的。 這將是什麼最好的方法?沒有兩次打開同一個文件?

+1

問題在哪裏? – lboshuizen

+0

您沒有詢問。 – Servy

+0

你有什麼試過?使用'Directory.GetFiles'獲取這些文件的列表,然後使用「parallel for」或任何你喜歡的東西來迭代。 – CodesInChaos

回答

0

這裏是一個小例子:

public static class FilesProcessor 
{ 
    private static List<FileProcessor> m_FileProcessors; 

    public static void Start() 
    { 
     m_FileProcessors = new List<FileProcessor>(); 

     for (Int32 year = 2005; year < DateTime.Now.Year; ++year) 
      InstanciateFileProcessor(year); 

     while (!FinishedLoading()) 
      Application.DoEvents(); 
    } 

    public static void Stop() 
    { 
     foreach (FileProcessor processor in m_FileProcessors) 
      processor.Stop() 

     m_FileProcessors.Clear(); 
     m_FileProcessors = null; 
    } 

    private static Boolean FinishedLoading() 
    { 
     foreach (FileProcessor processor in m_FileProcessors) 
     { 
      if (processor.IsAlive() && !processor.FinishedLoading()) 
       return false; 
     } 

     return true; 
    } 

    private static void InstanciateFileProcessor(Int32 year) 
    { 
     FileProcessor processor = new FileProcessor(year); 
     processor.Start(); 

     m_FileProcessors.Add(processor); 
    } 
} 

然後FileProcessor類:

public sealed class FileProcessor 
{ 
    private Int32 m_Year; 

    public Boolean IsAlive() 
    { 
     return ((m_Thread != null) && m_Thread.IsAlive); 
    } 

    public Boolean FinishedLoading() 
    { 
     return ((m_Thread == null) || m_Thread.Join(10)); 
    } 

    public FileProcessor(Int32 year) 
    { 
     m_Year = year; 

     m_Thread = new Thread(Load); 
     m_Thread.Name = "Background File Processor"; 
    } 

    public void Start() 
    { 
     if (m_Thread != null) 
      m_Thread.Start(); 
    } 

    public void Stop() 
    { 
     if ((m_Thread != null) && m_Thread.IsAlive) 
      m_Thread.Abort(); 
    } 

    private void Load() 
    { 
     // Browse the Year folder... 
     // Get and read all fines one by one... 
    } 
} 
+0

感謝您的幫助。糾正我,如果我worng,但你的代碼,每年只允許一個線程對嗎? – rukinhas

+0

是的......但您可以輕鬆編輯它以允許每個文件一個線程或任何您的需要!例如,您可以像這樣修改它:InstanciateFileProcessor(year,number)等等...... –

+0

謝謝!我會盡力實現這一點。 – rukinhas

1

可能的解決方案之一是使用生產者/消費者設計模式。

您的製作人將獲得一個文件列表並提供一些ProducerConsumer隊列。您的使用者將處理從隊列中取出的文件(或文件路徑)並對其進行處理(插入到數據庫中)。採用這種方法,每個文件只會被處理一次。

ProducerConsumer隊列的問題描述在C# producer/consumer SO問題中。

編輯

但是,這項任務可能會變得複雜如

  • 如果其中一個現有文件改變,會發生什麼情況。你是否必須用新的文件內容更新數據庫?如果是這樣,你將不得不有一個機制「標記」,說該文件已更改(文件的最後更新日期可能在某些情況下工作)
  • 如果在過程中添加新文件添加。等
+0

你好,謝謝你的回答。這些文件不會改變。如果添加新文件,我可以再次運行整個作業,或者我正在考慮保存已處理文件的文件名,以便我不必再次執行。感謝您的幫助 – rukinhas

+0

@rukinhas:在這種情況下,生產者/消費者設計模式應該適合您。請記住,處理線程(消費者)的數量不應太高(由於數量取決於硬件,操作系統等,因此不可能給出最佳線程數)。否則,你可能會失去一些性能。上下文切換。 – Tom

0

我會說1線程每年。 每個「年度線索」都會讀取以該年份號碼開頭的文件,並逐一讀取它們。 至於去到數據庫中,我會建議你要麼

  • 如果一切順利的一個表,刪除索引所以沒有索引鎖定發生,後來建立東印度
  • 如果無法刪除索引,至少使用行鎖定,並且等待時段用於transanctions超時之前(兩個或多個線程可同時被插入)

另一種解決方案,將是線程,以產生插入語句到一個文件,然後執行該文件來執行插入,或者您可以使用批量插入工具。但是,這取決於表的結構,你的DBMS

+0

我只有三年,從2010年開始到2012年結束,所以只有3個線程對我來說似乎很少...但是由於這是基於IO的,如果添加更多線程,我可能會遇到IO瓶頸...感謝幫助 – rukinhas

0

我可以在這裏看到兩種可能的方法。

首先,將問題分爲兩部分。 1 - 確定要處理的內容,2 - 處理。第1部分可能需要自行運行,因此您最終得到了需要處理的100%準確列表。然後你可以實現花哨的(或不是很花哨的)邏輯來分割列表和引入多個線程。

其次,做一些類似於@CarlosGrappa建議的東西。所以基本上你用自己的「預編程」過濾器創建每個線程。正如卡洛斯所說,這可能是今年。或者,您可以創建24個線程,每個文件時間戳一個小時。或者60個線程,每個線程都在一小時之後的某個特定分鐘內查看它基本上可以是任何給你一個明確的標準:(a)儘可能均勻地分割負載;(b)保證數據文件一次只處理一次。

很明顯,其中的第二種方法運行速度會更快,但您必須對如何分割文件進行一些額外的考慮。使用第一種方法,一旦獲得了完整列表,就可以基本上在處理器中一次性刪除100或1000或10000等文件,而不會過分關注如何執行這些文件。

0

使用.Net的並行類有什麼問題?

只是將一個集合傳遞給並行foreach循環。 .Net會爲您分配所有內容。您還可以傳入自定義分區程序,以便您可以使用塊分區。塊分區會導致線程繼續詢問更多任務。如果你不使用塊分區,所有的工作將被預先分配,從而當某些任務比其他任務花費更長時間(這可能導致某些線程處於空閒狀態,而一個線程仍有工作要做)時導致某些性能命中。

http://msdn.microsoft.com/en-us/library/dd460720.aspx

+0

我不知道這種方法!我將不得不閱讀有關信息並進行一些測試,但這似乎是解決我的問題的好方法。 – rukinhas

+0

這是迄今爲止最簡單的解決方案。只要你在循環中使用.Net的並行工具和集合,你應該沒問題。我只需要根據某些邏輯將數百萬個文件複製到某些位置,然後使用Parallel.ForEach並行處理它們。 – bjoern