0

私はプロジェクトに取り組んでいます。その一部は、指定されたフォルダー ファイルが読み取られます。プログラムは深いところまで移動し、ファイル名やその他の情報を収集して、それを自分の DFile クラスにラップし、さらに作業するためにコレクションに入れます。シングルスレッド(再帰読み取りを使用)の場合は機能しましたが、ディスクIOとマルチスレッドがパフォーマンスを向上させないことを無視して、マルチスレッドの観点からそれを行いたいと思います。学習目的で欲しいです。

これまでのところ、私はある決定から別の決定へとジャンプしており、計画を変更して、それがどのようになるか、うまくいくことはありません. あなたの助けをいただければ幸いです。

私が望むのは、ルートフォルダー名を指定し、プログラムがいくつかのミニスレッド (この目的のためにユーザー定義のスレッド数) を実行することです。各スレッドは、指定されたフォルダーの内容を読み取ります。スレッド コレクション - フォルダーが見つかると、フォルダーを (File オブジェクトとして) jobQueue に入れ、他の利用可能なスレッドがそのフォルダーで作業できるようにします。

このシステムを正しく取得できません。私はコードを変更して、静的コレクションを持つ 1 つのクラスから多くのクラスにどのようなクラスがあるべきかを考えてきました。これまでのところ、ここにリストしているクラスはほとんどありません。

DirectoryCrawler http://pastebin.com/8tVGpGT9

私の作品の残りは公開しません(プログラムの目的はここでは絶対にカバーされていないため、おそらく他のトピックで)。プログラムはフォルダーを読み取り、その中のファイルのリストを作成し、それを並べ替え (おそらくマルチスレッドも使用します)、同じハッシュされたファイルを検索し、それらの等しいファイル グループを結果ファイルに書き込むための作業スレッドが常に存在する必要があります。パフォーマンスを向上させる必要はありません。ファイルは小さくなります。最初は速度に取り組んでいたので、今は必要ありません。

読書のデザインに関する助けをいただければ幸いです

編集:

頭痛の種です:((。正しく動作しません:(これまでのところ:クローラー(1つのフォルダーを読み取るためのミニスレッドのように、見つかったファイルは他のクラスにあるfileListに移動し、フォルダーはキューに入れられます)pastebin.com/AkJLAUhD

スキャナークラス(実行可能かどうかさえわかりません)。DirectoryScanner (メイン、クローラーを制御し、メインのファイルリストを保持する必要があります) pastebin. com/2abGMgG9 .

DFile 自体はペーストビンです。com/8uqPWh6Z (ハッシングで何かがおかしくなりました。今、ソートするとすべて同じハッシュが得られます..うまくいきました.. (ハッシングは無関係な他のタスクのためです)) .

ebin を過ぎたファイルリスト。com/Q2yM6ZwS

テストコード:

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 は、テスト用のいくつかのファイルを含むフォルダーです

スキャナー自体を実行可能にするか、クローラーのみにするかは考えられません。スキャン中は、実際には並べ替えなどの他の作業を開始したくないためです(すべてのファイルを収集していないときに並べ替えるものがないため)..

4

2 に答える 2

3

Executorスレッドプールといくつかのクラスが必要です。

Fsearchクラス。これには、結果のコンテナが含まれています。また、Ffolderを返し、「foldersOutstanding」カウンターをカウントアップするファクトリメソッドと、「foldersOutstanding」をカウントダウンしてカウントバックするOnCompleteもあります。

フォルダを表すにはFfolderクラスが必要であり、そのパスがctorパラメータとして渡されます。Fsearchインスタンスとともにパラメーターとして提供されるフォルダーパスを反復するrunメソッドが必要です。

ルートフォルダを使用してFsearchを作成してロードし、プールに起動します。ルートパスとそのファイルを渡してフォルダクラスを作成し、それをロードします。次に、「searchComplete」イベントを待機します。

その最初のFfolderはそのフォルダーを反復処理し、「通常の」ファイルごとにDFileを作成(またはデプール)し、それらをFsearchコンテナーにプッシュします。フォルダが見つかると、Fsearchから別のFfolderを取得し、新しいパスをロードして、それをプールにもロードします。

Ffolderは、自身のフォルダーの反復処理を終了すると、FsearchのOnComplete'メソッドを呼び出します。OnCompleteは「foldersOutstanding」をカウントダウンしており、ゼロにデクリメントされると、すべてのフォルダーがスキャンされ、ファイルが処理されます。この最後のデクリメントを行ったスレッドは、Fsearchを続行できるように、searchCompleteイベントを通知します。Fsearchは、作成時に渡された「OnSearchComplete」イベントを呼び出すことができます。

言うまでもなく、Fsearchコールバックはスレッドセーフでなければなりません。

そのような演習は、学術的である必要はありません。すべてのDFileが配置されるFsearchのコンテナーは、生産者/消費者キューである可能性があります。他のスレッドは、検索が終了するまで待つのではなく、検索の進行中にDFileの処理を開始する可能性があります。

私は以前にこれを行ったことがありますが(Javaではありません)、問題なく動作します。このような設計では、複数の検索を並行して簡単に実行できます-一度に複数のハードドライブルートに対してFsearchを発行するのは楽しいです-ガタガタという音が印象的です

言うのを忘れた-そのような設計からの大きな利益は、待ち時間の長い複数のネットワークドライブを検索するときです。それらはすべて並行して検索できます。悲惨なシングルスレッドのシーケンシャル検索のスピードアップは何度もあります。シングルスレッド検索が1つのドライブのDFileのキューイングを終了するまでに、マルチ検索は4つのドライブを検索し、すでにほとんどのDFileが処理されています。

ノート:

1)上記のように厳密に実装されている場合、スレッドプールスレッドtahtはFSearchを実行し、検索が終了するまで「OnSearchComplete」イベントでブロックされるため、1つのスレッドを「使い果たします」。したがって、ライブFsearchインスタンスよりも多くのスレッドプールスレッドが存在する必要があります。そうでない場合、実際の検索を実行するためのスレッドは残りません(もちろん、私に起こったことです:)。

2)シングルスレッド検索とは異なり、結果は予測可能な順序または繰り返し可能な順序で返されません。たとえば、結果がGUIスレッドに入ったときに結果を通知し、それらをTreeViewに表示しようとすると、ツリービューコンポーネントを通るパスが結果ごとに異なる可能性があり、ビジュアルツリービューの更新に時間がかかります。これにより、Windows GUI入力キューがいっぱいになる可能性があります(10000制限)。これは、GUIが追いつかないか、Ffolderなどにオブジェクトプールを使用している場合、プールが空になり、パフォーマンスが低下する可能性があり、GUIスレッドが取得しようとすると空のプールから新しい検索を発行するためのFfolderなど、すべてのFfolderインスタンスがWindowsメッセージでスタックした場合のオールラウンドなデッドロック(はい、もちろん私に起こりました:)。そのようなことが起こらないようにするのが最善です!

例-私が見つけたこのようなもの-それはかなり古いWindows/C ++ Builderコードですが、それでも機能します-私はRad Studio 2009でそれを試し、すべてのレガシー/プロプライエタリガンジを削除し、いくつかのコメントを追加しました。ここで行うのは、例として、フォルダーとファイルをカウントアップすることだけです。'runnable'クラスは2つしかありません。myPool->submit()メソッドは、runnableをプールにロードし、そのrun()メソッドが実行されます。ベースコンストラクターには、'OnComplete' EventHander(TNotifyEvent)、delgateパラメーターがあります。これは、run()メソッドが戻るときにプールスレッドによって起動されます。

//******************************* クラス **************** ****************

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..
};

..そしてここにあります、動作しています:

コードが機能しています!

于 2012-04-14T12:17:59.853 に答える
0

実用的な実装を行ってマルチスレッドについて学習したい場合は、シングルスレッド アクティビティからマルチスレッド アクティビティへの切り替えが実際に理にかなっているものを選択することをお勧めします。

この場合、意味がありません。そして、それを実現するには、醜いコードを書く必要があります。これは、たとえば、1 つのスレッドが 1 つのサブフォルダー (ルート フォルダーの後の最初のレベル) だけを処理する可能性があるためです。しかし、200 個のサブフォルダーから始めるとどうなるでしょうか? またはそれ以上...その場合、200スレッドは意味がありますか? 疑わしい...

于 2012-04-14T11:34:03.163 に答える