2012-04-14 76 views
0

我正在開發項目。它的一部分是給定的文件夾文件。 程序深入並收集文件名和其他信息,我將其包裝到我自己的DFile類中,並將其存入集合中以供進一步工作。 它工作時是單線程的(使用遞歸讀取),但我想在多線程的角度來做到這一點,忽略磁盤IO和多線程不會提高性能的事情。我希望它用於學習目的。Java多線程和文件

到目前爲止,我一直在從一個決策跳到另一個決策,改變計劃將會如何並且無法實現。您的幫助將不勝感激。

我想要的是,我提供了根文件夾名稱,並且我的程序運行了幾個小線程(用於此目的的用戶定義線程數),每個線程讀取給定文件夾的內容: - 找到文件時,將其包裝到DFile並放入線程之間共享集合 - 當它找到文件夾時,將文件夾(作爲File對象)放入jobQueue中,讓其他可用的線程在其上工作。

我無法正確獲得此係統。我一直在改變代碼,想出從靜態集合到多個類的類應該是什麼類。 到目前爲止,幾節課我列出這裏:

DirectoryCrawler http://pastebin.com/8tVGpGT9

不會公佈我的工作休息(也許在其他的話題,因爲絕對不是這裏所涉及的程序的目的)。程序應該讀取文件夾並在其中創建一個文件列表,然後對其進行排序(我可能也會使用多線程),然後搜索相同的哈希文件,並且不斷有工作線程將這些相同的文件組寫入結果文件。我不需要獲得任何表現,文件會很小,因爲起初我正在努力提高速度,現在我不需要它了。

任何幫助,關於閱讀的設計,將不勝感激

編輯:

這麼多頭疼的:((不能正常工作:(這裏至今: 爬蟲(像讀一minithread一個文件夾,找到的文件轉到其他類中的fileList,並將文件夾放入隊列)pastebin com/AkJLAUhD

scanner類(甚至不知道它應該運行還是不運行)DirectoryScanner(main,should control爬蟲,保存主文件列表)pastebin。com/2abGMgG9。

DFile本身的pastebin。 com/8uqPWh6Z(哈希的東西變成了錯誤,現在當排序所有相同的哈希..工作..(哈希是其他任務無關))。

過去的ebin文件列表。 COM/Q2yM6ZwS

testcode:

DirectoryScanner reader = new DirectoryScanner(4); 
for (int i = 0; i < 4; i ++) { 
    reader.runTask(new DirectoryCrawler("myroot", reader)); 
} 
try { 
    reader.kill(); 
    while (!reader.isDone()) { 
     System.out.println("notdone"); 
    } 
    reader.getFileList().print(); 
} 

myroot是一些文件的文件夾用於測試

什麼,我甚至不能想象的要掃描儀本身運行的,或者只爬蟲。因爲在掃描時我真的不想開始做其他東西比如排序(因爲沒有收集所有文件的時候沒有排序)..

+0

這是錯誤? – SJuan76 2012-04-14 11:26:57

+0

對此沒有正確的答案,程序員可能是一個更好的地方,但你最主要的問題是你似乎試圖在一個線程中做太多。如果是我,我會有一個具有路徑參數/屬性的任務隊列,而不是路徑/文件列表。 – 2012-04-14 11:32:53

+0

@TonyHopkinson - 是的,這是如何做 - 排隊文件夾任務。 – 2012-04-14 12:44:54

回答

4

您需要的執行人線程池和一些類:

一個Fsearch類。這包含您的容器的結果。它還有一個工廠方法,它返回一個Ffolder,計算一個'foldersOutstanding'計數器,以及一個通過倒計數'foldersOutstanding'來計算它們的OnComplete:

您需要一個Ffolder類來表示一個文件夾並被傳遞其路徑作爲ctor參數。它應該有一個迭代的運行方法是作爲參數提供的文件夾路徑以及Fsearch實例。

使用根文件夾創建並加載Fsearch並將其放入池中。它創建一個文件夾類,傳遞其根路徑和itslef,並加載它。然後它等待一個'searchComplete'事件。

第一個文件夾迭代其文件夾,爲每個'普通'文件創建DFiles,並將它們推送到Fsearch容器中。如果它找到一個文件夾,它將從Fsearch獲取另一個Ffolder,並將其加載到新的路徑中,並將其加載到池中。

當Ffolder完成迭代自己的文件夾時,它會調用Fsearch的OnComplete方法。 OnComplete正在對'foldersOutstanding'進行倒計時,當它減到零時,所有文件夾都被掃描並處理文件。執行此最終遞減的線程會標記searchComplete事件,以便Fsearch可以繼續。 Fsearch可以調用創建時傳遞的一些「OnSearchComplete」事件。

毫無疑問,Fsearch回調必須是線程安全的。

這樣的練習不一定要學術。 Fsearch中的所有DFiles所在的容器可以是生產者 - 消費者隊列。其他線程可以在搜索過程中開始處理DFiles,而不是等到結束。

我以前做過這個(但不是在Java中) - 它工作正常。像這樣的設計可以輕鬆地並行執行多個搜索 - 同時發佈多個硬盤驅動器根目錄的Fsearch非常有趣 - 咔嗒聲令人印象深刻

忘了說 - 這種設計的巨大收益是當搜索幾個網絡驅動器具有高延遲。他們都可以並行搜索。一個悲慘的單線程順序搜索加速很多次。當單線程完成一個驅動器的DFiles排隊時,多搜索搜索了四個驅動器,並且已經處理了大部分DFiles。

注:

1)如果如上述嚴格執行,線程池線程taht執行FSearch被阻止對「OnSearchComplete」事件直到搜索已經結束,因此「使用了」一個線程。因此,必須有比實時Fsearch實例多的線程池線程,否則將不會有線程留下來進行實際搜索(當然,這發生在我身上:))。

2)與單線程搜索不同,結果不會以任何可預測或可重複的順序返回。例如,如果您將結果標示爲GUI線程並嘗試在TreeView中顯示它們,則通過treeview組件的路徑對於每個結果可能會有所不同,更新可視化樹視圖將會很長。這可能會導致Windows GUI輸入隊列變滿(10000限制),因爲GUI無法跟上,或者如果使用Ffolder的對象池等,池可能會清空,性能下降,並且如果GUI線程嘗試獲取一個Ffolder從空池中發出一個新的搜索並阻止,所有Ffolder實例在Windows消息中出現全面死鎖,(當然,這發生在我身上:)。最好不要讓這種事情發生!

示例 - 類似這樣的事情我發現 - 這是非常舊的Windows/C++ Builder代碼,但它仍然有效 - 我在Rad Studio 2009上試用了它,刪除了所有舊版/專有gunge並添加了一些額外的評論。這裏所做的只是計算文件夾和文件,就像一個例子。只有幾個'runnable'類myPool-> submit()方法將一個runnable加載到池中,並且run()方法被執行。基本ctor有一個'OnComplete'EventHander,(TNotifyEvent),delgate參數 - 當run()方法返回時,由pool線程觸發。

// ******************************* CLASSES ************ ********************

class DirSearch; // forward dec. 

class ScanDir:public PoolTask{ 
    String FmyDirPath; 
    DirSearch *FmySearch; 
    TStringList *filesAndFolderNames; 
public:        // Counts for FmyDirPath only 
    int fileCount,folderCount; 
    ScanDir(String thisDirPath,DirSearch *mySearch); 
    void run();      // an override - called by pool thread 
}; 


class DirSearch:public PoolTask{ 
    TNotifyEvent FonComplete; 
    int dirCount; 
    TEvent *searchCompleteEvent; 
    CRITICAL_SECTION countLock; 
public: 
    String FdirPath; 
    int totalFileCount,totalFolderCount; // Count totals for all ScanDir's 

    DirSearch(String dirPath, TNotifyEvent onComplete); 
    ScanDir* getScanDir(String path);  // get a ScanDir and inc's count 
    void run();       // an override - called by pool thread 
    void __fastcall scanCompleted(TObject *Sender); // called by ScanDir's 
}; 

// ********************* **********方法********************************

// ctor - just calls base ctor an initialzes stuff.. 
ScanDir::ScanDir(String thisDirPath,DirSearch *mySearch):FmySearch(mySearch), 
     FmyDirPath(thisDirPath),fileCount(0),folderCount(0), 
     PoolTask(0,mySearch->scanCompleted){}; 


void ScanDir::run() // an override - called by pool thread 
{ 
// fileCount=0; 
// folderCount=0; 
    filesAndFolderNames=listAllFoldersAndFiles(FmyDirPath); // gets files 
    for (int index = 0; index < filesAndFolderNames->Count; index++) 
    { // for all files in the folder.. 
     if((int)filesAndFolderNames->Objects[index]&faDirectory){ 
      folderCount++; //do count and, if it's a folder, start another ScanDir 
      String newFolderPath=FmyDirPath+"\\"+filesAndFolderNames->Strings[index]; 
      ScanDir* newScanDir=FmySearch->getScanDir(newFolderPath); 
      myPool->submit(newScanDir); 
     } 
     else fileCount++; // inc 'ordinary' file count 
    } 
    delete(filesAndFolderNames); // don't leak the TStringList of filenames 
}; 

DirSearch::DirSearch(String dirPath, TNotifyEvent onComplete):FdirPath(dirPath), 
    FonComplete(onComplete),totalFileCount(0),totalFolderCount(0),dirCount(0), 
    PoolTask(0,onComplete) 
{ 
    InitializeCriticalSection(&countLock); // thread-safe count 
    searchCompleteEvent=new TEvent(NULL,false,false,"",false); // an event 
             // for DirSearch to wait on till all ScanDir's done 
}; 

ScanDir* DirSearch::getScanDir(String path) 
{ // up the dirCount while providing a new DirSearch 
    EnterCriticalSection(&countLock); 
    dirCount++; 
    LeaveCriticalSection(&countLock); 
    return new ScanDir(path,this); 
}; 

void DirSearch::run() // called on pool thread 
{ 
    ScanDir *firstScanDir=getScanDir(FdirPath); // get first ScanDir for top 
    myPool->submit(firstScanDir);    // folder and set it going 
    searchCompleteEvent->WaitFor(INFINITE);  // wait for them all to finish 
} 

/* NOTE - this is a DirSearch method, but it's called by the pool threads 
running the DirScans when they complete. The 'DirSearch' pool thread is stuck 
on the searchCompleteEvent, waiting for all the DirScans to complete, at which 
point the dirCount will be zero and the searchCompleteEvent signalled. 
*/ 
void __fastcall DirSearch::scanCompleted(TObject *Sender){ // a DirSearch done 
    ScanDir* thiscan=(ScanDir*)Sender; // get the instance that completed back 
    EnterCriticalSection(&countLock); // thread-safe 
    totalFileCount+=thiscan->fileCount;  // add DirSearch countst to totals 
    totalFolderCount+=thiscan->folderCount; 
    dirCount--;       // another one gone.. 
    LeaveCriticalSection(&countLock); 
    if(!dirCount) searchCompleteEvent->SetEvent(); // if all done, signal 
    delete(thiscan);      // another one bites the dust.. 
}; 

..在這裏,工作:

Code working!

+0

非常感謝,我正在進行:)) – Igor222 2012-04-14 15:38:14

+0

祝你好運 - 我編輯了一些我記得從我的經驗中得到的點,(不愉快,尖銳的點:) – 2012-04-14 16:17:25

+0

有些地方有點難以理解。請在我的第一篇文章中添加一些信息。請檢查我的來源,會非常感激。我完全累了,整天都無法工作。花了一整天的時間用平行的方式進行排序,並沒有得到任何東西,刪除了各種各樣的... – Igor222 2012-04-14 19:45:35

0

如果你想通過做一些實際的實現來學習一些多線程,最好挑選一些從單線程活動切換到多線程活動實際上有意義的東西。

在這種情況下,它沒有任何意義。而要實現它,需要編寫一些難看的代碼片段。這是因爲,例如,一個線程只能處理一個子文件夾(根文件夾之後的第一級)。但是如果你從200個子文件夾開始呢?或者更多...在這種情況下200線程是否有意義?我對此表示懷疑......

+0

我想我提到有固定的userdefined線程數。因此,如果每個線程在變得可用時都將隊列中的其他文件夾並對其進行處理。我真的知道它不會幫助,但即使是給我的人說他需要看我如何實現多線程,不關心性能等。 – Igor222 2012-04-14 11:38:58

+0

如果「男人」需要看你如何實現多線程,爲什麼你不是在程序的另一部分實現它(它會更有意義)?這就是我想說的...... – 2012-04-14 12:04:41

+0

它可以說得通!處理找到的文件不必等到搜索完成後 - 如果DFile的容器是生產者 - 消費者隊列,則只要第一個準備好就可以開始處理。可以並行運行多個驅動器的搜索 - 這對於多個網絡驅動器非常重要。 – 2012-04-14 12:22:30