首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >多线程读取大量文件

多线程读取大量文件
EN

Stack Overflow用户
提问于 2012-05-16 18:58:07
回答 4查看 29.6K关注 0票数 16

我仍然在思考Java中并发是如何工作的。我理解(如果您订阅OOJava5并发模型),您将使用一个TaskCallable (分别使用run()call()方法),并且应该尽可能多地并行化该实现的方法。

但是,我仍然不了解Java中并发编程的一些固有内容:

  • Taskrun()方法如何分配正确的并发工作量?

作为一个具体的例子,如果我有一个I/O绑定的readMobyDick()方法,将赫尔曼·梅尔维尔的“白鲸”的全部内容从本地系统上的文件读入内存中,该怎么办?假设我希望这个readMobyDick()方法是并发的,并由3个线程处理,其中:

memory

  • Thread #2将书的前1/3读入memory

  • Thread #2将书的第2/3读为memory

  • Thread #3将书的最后1/3读入内存

我是否需要将“白鲸”合并成三个文件,并将每个文件传递给各自的任务?还是我只是在已实现的readMobyDick() run() 方法中调用 Executor ,并且(不知怎么)Executor知道如何在线程之间分解工作。

我是一个非常视觉的学习者,所以任何代码的例子正确的方法,这是非常感谢的!谢谢!

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-05-16 19:16:20

你可能意外地选择了一个绝对最糟糕的平行活动的例子!

从单个机械磁盘并行读取实际上比用单个线程读取要慢,因为实际上,当每个线程开始运行时,您正在将机械头弹到磁盘的不同部分。最好将其保留为单个线程活动。

让我们以另一个例子为例,它与您的类似,但实际上可以提供一些好处:假设我想在一个巨大的单词列表中搜索某个单词的出现(这个列表甚至可能来自一个磁盘文件,但正如我所说的,通过一个线程读取)。假设我可以使用你的例子中的3个线程,每个线程在庞大的单词列表的1/3上搜索,并保持一个本地计数器,显示搜索单词出现的次数。

在本例中,您需要将列表划分为3部分,将每个部分传递给一个类型为Runnable并在run方法中实现搜索的不同对象。

运行时本身不知道如何进行分区或诸如此类的操作,您必须自己指定。还有许多其他的分区策略,每种策略都有自己的优点和缺点,但我们现在可以坚持静态分区。

让我们看看一些代码:

代码语言:javascript
运行
复制
class SearchTask implements Runnable {
     private int localCounter = 0;
     private int start; // start index of search
     private int end;
     private List<String> words;
     private String token;

     public SearchTask(int start, int end, List<String> words, String token) {
         this.start = start;
         this.end = end;
         this.words = words;
         this.token = token;
     }

     public void run() {
         for(int i = start; i < end; i++) {
              if(words.get(i).equals(token)) localCounter++;
         }
     }

     public int getCounter() { return localCounter; }
}

// meanwhile in main :)

List<String> words = new ArrayList<String>();
// populate words 
// let's assume you have 30000 words

// create tasks
SearchTask task1 = new SearchTask(0, 10000, words, "John");
SearchTask task2 = new SearchTask(10000, 20000, words, "John");
SearchTask task3 = new SearchTask(20000, 30000, words, "John");

// create threads for each task
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
Thread t3 = new Thread(task3);

// start threads
t1.start();
t2.start();
t3.start();

// wait for threads to finish
t1.join();
t2.join();
t3.join();

// collect results
int counter = 0;
counter += task1.getCounter();
counter += task2.getCounter();
counter += task3.getCounter();

这应该很好用。注意,在实际情况下,您将构建一个更通用的分区方案。如果您希望返回结果,可以使用ExecutorService并实现Callable而不是Runnable

因此,另一个使用更高级构造的示例:

代码语言:javascript
运行
复制
class SearchTask implements Callable<Integer> {
     private int localCounter = 0;
     private int start; // start index of search
     private int end;
     private List<String> words;
     private String token;

     public SearchTask(int start, int end, List<String> words, String token) {
         this.start = start;
         this.end = end;
         this.words = words;
         this.token = token;
     }

     public Integer call() {
         for(int i = start; i < end; i++) {
              if(words.get(i).equals(token)) localCounter++;
         }
         return localCounter;
     }        
}

// meanwhile in main :)

List<String> words = new ArrayList<String>();
// populate words 
// let's assume you have 30000 words

// create tasks
List<Callable> tasks = new ArrayList<Callable>();
tasks.add(new SearchTask(0, 10000, words, "John"));
tasks.add(new SearchTask(10000, 20000, words, "John"));
tasks.add(new SearchTask(20000, 30000, words, "John"));

// create thread pool and start tasks
ExecutorService exec = Executors.newFixedThreadPool(3);
List<Future> results = exec.invokeAll(tasks);

// wait for tasks to finish and collect results
int counter = 0;
for(Future f: results) {
    counter += f.get();
}
票数 22
EN

Stack Overflow用户

发布于 2012-05-16 19:44:41

你选了一个不好的例子,都铎很好地指出。旋转磁盘硬件受移动盘和磁头的物理约束,最有效的读取实现是按顺序读取每个块,这减少了移动磁头或等待磁盘对齐的需要。

尽管如此,有些操作系统并不总是不断地将东西存储在磁盘上,对于那些记得的人来说,如果OS /文件系统没有为您完成任务,碎片整理可以提供磁盘性能的提升。

正如您提到的,想要一个有益的程序,让我建议一个简单的,矩阵加法。

假设您为每个核心创建了一个线程,则可以将要添加到N(每个线程的一个矩阵)行中的任意两个矩阵分成几个部分。矩阵加法(如果您还记得的话)是这样工作的:

代码语言:javascript
运行
复制
A + B = C

代码语言:javascript
运行
复制
[ a11, a12, a13 ]   [ b11, b12, b13]  =  [ (a11+b11), (a12+b12), (a13+c13) ]
[ a21, a22, a23 ] + [ b21, b22, b23]  =  [ (a21+b21), (a22+b22), (a23+c23) ]
[ a31, a32, a33 ]   [ b31, b32, b33]  =  [ (a31+b31), (a32+b32), (a33+c33) ]

因此,要在N个线程之间分发它,我们只需将行数和模数除以线程数,就可以得到它将添加的“线程id”。

代码语言:javascript
运行
复制
matrix with 20 rows across 3 threads
row % 3 == 0 (for rows 0, 3, 6,  9, 12, 15, and 18)
row % 3 == 1 (for rows 1, 4, 7, 10, 13, 16, and 19)
row % 3 == 2 (for rows 2, 5, 8, 11, 14, and 17)
// row 20 doesn't exist, because we number rows from 0

现在每个线程都“知道”它应该处理哪些行,并且结果“每行”可以很小地计算,因为结果不会进入其他线程的计算域。

现在所需要的只是一个“结果”数据结构,它跟踪何时计算了值,何时设置了最后一个值,然后计算就完成了。在这个由两个线程组成的矩阵加法结果的“假”例子中,用两个线程计算答案大约需要一半的时间。

代码语言:javascript
运行
复制
// the following assumes that threads don't get rescheduled to different cores for 
// illustrative purposes only.  Real Threads are scheduled across cores due to
// availability and attempts to prevent unnecessary core migration of a running thread.
[ done, done, done ] // filled in at about the same time as row 2 (runs on core 3)
[ done, done, done ] // filled in at about the same time as row 1 (runs on core 1)
[ done, done, .... ] // filled in at about the same time as row 4 (runs on core 3)
[ done, ...., .... ] // filled in at about the same time as row 3 (runs on core 1)

更复杂的问题可以通过多线程来解决,不同的问题可以用不同的技术解决。我特意挑了一个最简单的例子。

票数 2
EN

Stack Overflow用户

发布于 2012-05-16 19:45:13

(分别用run()或call()方法实现一个任务或可调用的任务,它要求尽可能多地并行化该实现的方法。

Task表示一个离散的工作单位。

将文件加载到内存是一个离散的工作单元,因此可以将此活动委托给后台线程。例如,后台线程运行这个加载文件的任务。

它是一个离散的工作单元,因为它没有执行其工作所需的其他依赖项(加载文件),并且具有离散的边界。

你所要求的是把它进一步划分为任务。例如,一个线程加载文件的1/3,而另一个线程加载2/3等。

如果您能够将任务划分为进一步的子任务,那么根据定义,它首先不会是一个任务。因此,加载文件本身就是一项任务。

举个例子:

假设您有一个GUI,您需要向用户展示来自5个不同文件的数据。要显示它们,还需要准备一些数据结构来处理实际数据。

所有这些都是不同的任务。

例如,文件的加载是5个不同的任务,因此可以由5个不同的线程来完成。

数据结构的准备可以用不同的线程来完成。

当然,GUI在另一个线程中运行。

所有这些都可以同时发生。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10624899

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档