【问题标题】:Java multithreading and filesJava多线程和文件
【发布时间】:2012-04-26 13:05:54
【问题描述】:

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

到目前为止,我一直在从一个决定跳到另一个决定,改变计划的方式,但不能把它做好。我们将不胜感激。

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

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

目录爬虫http://pastebin.com/8tVGpGT9

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

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

编辑:

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

scanner 类(甚至不知道它是否可以运行)。 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 是一个包含一些测试文件的文件夹

任何东西,我什至想不出扫描仪本身应该是可运行的,还是只能是爬虫。因为在扫描时,我实际上不想开始做其他的事情,比如排序(因为没有收集所有文件,没有任何东西可以排序)..

【问题讨论】:

  • 对此没有正确的答案,程序员可能是一个更好的地方,但你的主要问题是你似乎试图在一个线程中做太多事情。如果是我,我会有一个带有路径参数/属性的任务队列,而不是路径/文件列表。
  • @TonyHopkinson - 是的,就是这样做的 - 排队文件夹任务。

标签: java multithreading concurrency io directory


【解决方案1】:

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

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

【讨论】:

  • 我想我提到了用户定义的固定线程数。因此,如果每个线程可用时,它会占用队列中的其他文件夹并处理它。我真的知道这无济于事,但即使是给我的人也说他需要看看我如何实现多线程,不关心性能等。
  • 如果“人”需要看看你是如何实现多线程的,你为什么不在程序的另一部分实现它(那里更有意义)?这就是我想说的......
  • 有道理!找到的文件的处理不必等到搜索完成 - 如果 DFile 的容器是生产者 - 消费者队列,则处理可以在第一个准备好后立即开始。多个驱动器的搜索可以并行运行 - 这在多个网络驱动器的情况下非常重要。
【解决方案2】:

你需要 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 上尝试过,删除了所有遗留/专有工具并添加了一些额外的 cmets。它在这里所做的只是计算文件夹和文件,就像一个例子一样。只有几个“可运行”类 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..
};

..它在这里,工作:

【讨论】:

  • 非常感谢,我正在处理中:))
  • 祝你好运 - 我编辑了我从经验中记得的几点,(不愉快,尖锐的点:)
  • 在某些方面有点难以理解。请在我的第一篇文章中添加一些信息。请检查我的来源,将非常非常感激。我真的很累,一整天都不能上班。半天的时间都花在了并行排序上,一无所获,把所有的东西都删掉了……
  • 不错的答案,一旦我停止犯错,我希望能在它附近的某个地方。 +1 努力
猜你喜欢
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 1970-01-01
  • 2012-05-23
相关资源
最近更新 更多