Java多线程和文件

时间:2012-04-14 11:18:29

标签: java multithreading concurrency io directory

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

到目前为止,我已经从一个决定跳到另一个决策,改变计划将会如何,并且不能让它变好。感谢您的帮助。

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

我无法正确使用此系统。我一直在改变代码,从一个具有静态集合的类到多个类中提出应该是什么类。 到目前为止,我列出的课程很少:

DirectoryCrawler http://pastebin.com/8tVGpGT9

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

任何有关阅读设计的帮助都将受到赞赏

编辑:

这么多头痛:((。工作不正常:(到目前为止: crawler(就像一个用于读取一个文件夹的minithread,找到的文件转到其他类中的fileList,以及要排队的文件夹)pastebin。 COM / AkJLAUhD

扫描仪类(甚至不知道它应该是否可运行)。 DirectoryScanner(主要,应该控制爬虫,保存主文件列表)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是一个包含一些测试文件的文件夹

任何事情,我甚至都不会想到扫描仪本身应该可以运行,还是只有爬虫。因为虽然扫描我实际上不想开始做其他东西,如排序(因为没有收集而没有收集所有文件)..

2 个答案:

答案 0 :(得分:3)

您需要Executor线程池和一些类:

Fsearch类。这包含结果的容器。它还有一个工厂方法,它返回一个Ffolder,计算'foldersOutstanding'计数器,以及一个OnComplete,通过倒计数'foldersOutstanding'来重新计算它们:

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

使用根文件夹创建并加载Fsearch并将其激活到池中。它创建一个文件夹类,传递其根路径和itslef,并加载它。然后它等待'searchComplete'事件。

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

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

几乎不用说Fsearch回调必须是线程安全的。

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

我以前做过这个,(但不是用Java做过),它运行正常。这样的设计可以轻松地并行进行多次搜索 - 同时为多个硬盘驱动器根发出一个Fsearch很有趣 - 这种咔嗒声很令人印象深刻

忘了说 - 这种设计的巨大收获是在搜索具有高延迟的多个网络驱动器时。它们都可以并行搜索。可怜的单线程顺序搜索的加速很多次。当单线程搜索完成为一个驱动器排队DFile时,多搜索已经搜索了四个驱动器,并且已经处理了大部分DF。

注:

1)如果严格执行如上所述,线程池线程taht执行FSearch在'OnSearchComplete'事件上被阻止,直到搜索结束,因此'用完'一个线程。因此,必须有比实时Fsearch实例更多的线程池线程,否则将不会留下任何线程来进行实际搜索(是的,当然这发生在我身上:)。

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

示例 - 我发现这样的东西 - 它是相当旧的Windows / C ++ Builder代码但它仍然有效 - 我在我的Rad Studio 2009上尝试过它,删除了所有遗留/专有gunge并添加了一些额外的注释。这里所做的只是计算文件夹和文件,就像一个例子。只有几个'runnable'类myPool-&gt; submit()方法将runnable加载到池中,并且run()方法被执行。基本ctor有一个'OnComplete'EventHander,(TNotifyEvent),delgate参数 - 当run()方法返回时由池线程触发。

// ******************************* 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!

答案 1 :(得分:0)

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

在这种情况下,它没有任何意义。而且,要实现它,需要编写一些丑陋的代码。这是因为你可以有一个线程只处理一个子文件夹(根文件夹后的第一级)。但是如果你从200个子文件夹开始怎么办?或者更多......在这种情况下200线程是否有意义?我怀疑......