0

我正在做项目。其中一部分是读取给定的文件夹文件。程序深入并收集我包装到我自己的 DFile 类中的文件名和其他信息,并将其放入集合中以供进一步工作。它在单线程(使用递归读取)时工作,但我想从多线程的角度来做,忽略磁盘 IO 和多线程不会提高性能的事情。我想要它用于学习目的。

到目前为止,我一直在从一个决定跳到另一个决定,改变计划如何去做,但不能做得很好。您的帮助将不胜感激。

我想要什么,我提供根文件夹名称,并且我的程序运行几个小线程(为此目的,用户定义的线程数),每个线程读取给定的文件夹内容: - 当它找到文件时,将其包装到 DFile 并放入共享之间线程集合 - 当它找到文件夹时,将文件夹(作为文件对象)放入 jobQueue,以便其他可用线程对其进行处理。

我无法正确获取此系统。我一直在更改代码,考虑应该从一个具有静态集合的类到多个类。到目前为止,我在这里列出的几类:

DirectoryCrawler http://pastebin.com/8tVGpGT9

不会发布我的其余工作(可能在其他主题中,因为这里绝对没有涵盖程序的目的)。程序应该读取文件夹并在其中列出文件列表,然后对其进行排序(我可能也会使用多线程),然后搜索相同的散列文件,并且有不断工作的线程将这些相同的文件组写入结果文件。我不需要获得任何性能,文件会很小,因为一开始我正在研究速度,我现在不需要它。

任何有关阅读设计的帮助将不胜感激

编辑:

非常头疼:((。不能正常工作:(到目前为止:爬虫(就像一个用于读取一个文件夹的迷你线程,找到的文件进入其他类的fileList,以及进入队列的文件夹)pastebin.com/AkJLAUhD

扫描仪类(甚至不知道它是否可以运行)。DirectoryScanner(主要,应该控制爬虫,保存主要文件列表)pastebin。com/2abGMgG9 。

DFile 本身 pastebin。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”计数器进行计数,以及一个 OnComplete,它通过对“foldersOutstanding”进行计数来重新计算它们:

您需要一个 Ffolder 类来表示一个文件夹,并将其路径作为 ctor 参数传递。它应该有一个迭代的运行方法是作为参数提供的文件夹路径以及 Fsearch 实例。

使用根文件夹创建并加载一个 Fsearch 并将其发射到池中。它创建一个文件夹类,传递它的根路径和它的文件,然后加载它。然后它等待“searchComplete”事件。

第一个 Ffolder 迭代其文件夹,为每个“普通”文件创建(或分离)DFiles,并将它们推送到 Fsearch 容器中。如果它找到一个文件夹,它会从 Fsearch 中获取另一个 Ffolder,用新路径加载它,并将其加载到池中。

当 Ffolder 完成对自己的文件夹的迭代时,它会调用 Fsearch 的 OnComplete' 方法。OnComplete 正在倒计时“foldersOutstanding”,当它减到零时,所有文件夹都已被扫描并已处理文件。执行此最终减量的线程向 searchComplete 事件发出信号,以便 Fsearch 可以继续。Fsearch 可以调用一些在创建时传递的“OnSearchComplete”事件。

毫无疑问,Fsearch 回调必须是线程安全的。

这样的练习不一定是学术性的。所有 DFile 所在的 Fsearch 中的容器可能是一个生产者-消费者队列。其他线程可以在搜索过程中开始处理 DFile,而不是等到结束。

我以前做过这个(但不是在 Java 中),它工作正常。像这样的设计可以轻松地并行执行多个搜索 - 一次发出多个硬盘驱动器根的 Fsearch 很有趣 - 咔哒声令人印象深刻

忘了说 - 这种设计的最大好处是在搜索多个具有高延迟的网络驱动器时。它们都可以并行搜索。单线程顺序搜索的速度提升了很多倍。当单线程搜索完成对一个驱动器的 DFile 的排队时,多重搜索已经搜索了四个驱动器并且已经处理了大部分 DFile。

笔记:

1) 如果按照上面严格执行,执行 FSearch 的线程池线程在 'OnSearchComplete' 事件上被阻塞,直到搜索结束,所以'用完'一个线程。因此,线程池线程必须比活动的 Fsearch 实例多,否则将没有剩余线程来进行实际搜索(是的,这当然发生在我身上:)。

2) 与单线程搜索不同,结果不会以任何可预测或可重复的顺序返回。例如,如果您在结果进入 GUI 线程时发出信号并尝试在 TreeView 中显示它们,则每个结果通过 treeview 组件的路径可能会有所不同,更新可视化 treeview 会很长。这可能会导致 Windows GUI 输入队列变满(10000 个限制),因为 GUI 无法跟上,或者,如果将对象池用于 Ffolder 等,池可能会清空,从而降低性能,并且如果 GUI 线程尝试获取一个 Ffolder 从空池中发出新的搜索等阻塞,所有 Ffolder 实例都卡在 Windows 消息中的全面死锁(是的,当然这发生在我身上:)。最好不要让这样的事情发生!

示例 - 我发现类似这样的东西 - 它是相当旧的 Windows/C++ Builder 代码,但它仍然有效 - 我在我的 Rad Studio 2009 上尝试过,删除了所有遗留/专有工具并添加了一些额外的注释。它在这里所做的只是计算文件夹和文件,就像一个例子一样。只有几个“可运行”类 myPool->submit() 方法将一个可运行对象加载到池中,然后它的 run() 方法被执行。基本 ctor 有一个 '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

如果你想通过一些实际的实现来学习一些多线程,最好选择从单线程活动切换到多线程活动实际上有意义的东西。

在这种情况下,它没有任何意义。而且,要实现它,需要编写一些难看的代码。这是因为您可以让一个线程仅处理一个子文件夹(根文件夹之后的第一级)。但是如果你从 200 个子文件夹开始呢?或者更多......在这种情况下200个线程有意义吗?我对此表示怀疑...

于 2012-04-14T11:34:03.163 回答