我的多线程代码并没有 运行 更快
My multi-threading code doesn't run faster
我的电脑是8核64G内存。我使用多线程的方式是否如下所示?它从每个文档(filePath)中处理一行并将结果存储到文档列表中,最后 return.
问题是,当设置“4”个线程时,我没有看到 运行ning 更快。我在测试数据上花费的时间总是与单线程所需的时间相同 运行。我使用 Callable 的方式有什么问题吗?
List<Document> processDocs(String filePath) {
ExecutorService pool = Executors.newFixedThreadPool(4);
List<Document> processedDocs = new ArrayList<>();
try {
br = new BufferedReader(IOUtils.fileReader(filePath));
String line = null;
int docNo=0;
while ((line = br.readLine()) != null) {
line = line.trim();
Callable<Document> callable = new NLPTextThread(line, ++docNo);
Future<Document> future = pool.submit(callable);
try {
processedDocs.add(future.get());
} catch (InterruptedException e) {
log.error("InterruptedException " + line);
} catch (ExecutionException e) {
log.error("ExecutionException: " + line);
e.printStackTrace();
}
}
pool.shutdown();
return processedDocs;
}
已编辑:关于 'docNo' 变量的线程安全的另一个问题。我想将文档序列号传递给 Callable。在这种情况下,"docNo" 变量是线程安全的吗?
processedDocs.add(future.get());
该行等待工作人员完成作业,然后再从文件中读取另一行。您应该将 futures 保存到列表中,然后在阅读完成后将它们全部获取。
更喜欢
futures.add(future);
...
// while loop exits
for (Future<Document> future : futures) {
try {
processedDocs.add(future.get());
} catch (InterruptedException e) {
} catch (ExecutionException e) {
e.printStackTrace();
}
}
您在提交可调用对象后立即调用 future.get()
这一事实使您的代码有效地单线程化为 get
块,并且您不会在将来向池中提交更多任务已解决。在一个循环中提交所有内容并存储期货。然后迭代期货列表以收集结果。
这个更好:
List<Document> processDocs(String filePath) {
List<Callable<Document>> tasks = new ArrayList<>();
try {
BufferedReader br = new BufferedReader(IOUtils.fileReader(filePath));
String line = null;
while ((line = br.readLine()) != null) {
tasks.add(new NLPTextThread(line.trim());
}
ExecutorService executor = Executors.newfixedThreadPool(4);
return executor.invokeAll(tasks)
.stream()
.map(future -> {
try {
return future.get();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}).collect(Collectors.toList());
}
PS.
我想我也会 强调 评论者在原始问题上提出的 IO 问题。
如果您的 NLPTextThread
文件每一行的执行时间可以忽略不计(与从文件中读取这一行所花费的时间相比),我认为您不会看到 显着 改进 运行 使用线程池作为 IO 的时间在这种情况下实际上是一个瓶颈,因为您正在主线程(单线程)上读取单个大文件。如果将输入(如果很大)拆分为多个文件并并行处理 每个文件,您可能会看到更高的性能提升。仅供参考。
我的电脑是8核64G内存。我使用多线程的方式是否如下所示?它从每个文档(filePath)中处理一行并将结果存储到文档列表中,最后 return.
问题是,当设置“4”个线程时,我没有看到 运行ning 更快。我在测试数据上花费的时间总是与单线程所需的时间相同 运行。我使用 Callable 的方式有什么问题吗?
List<Document> processDocs(String filePath) {
ExecutorService pool = Executors.newFixedThreadPool(4);
List<Document> processedDocs = new ArrayList<>();
try {
br = new BufferedReader(IOUtils.fileReader(filePath));
String line = null;
int docNo=0;
while ((line = br.readLine()) != null) {
line = line.trim();
Callable<Document> callable = new NLPTextThread(line, ++docNo);
Future<Document> future = pool.submit(callable);
try {
processedDocs.add(future.get());
} catch (InterruptedException e) {
log.error("InterruptedException " + line);
} catch (ExecutionException e) {
log.error("ExecutionException: " + line);
e.printStackTrace();
}
}
pool.shutdown();
return processedDocs;
}
已编辑:关于 'docNo' 变量的线程安全的另一个问题。我想将文档序列号传递给 Callable。在这种情况下,"docNo" 变量是线程安全的吗?
processedDocs.add(future.get());
该行等待工作人员完成作业,然后再从文件中读取另一行。您应该将 futures 保存到列表中,然后在阅读完成后将它们全部获取。
更喜欢
futures.add(future);
...
// while loop exits
for (Future<Document> future : futures) {
try {
processedDocs.add(future.get());
} catch (InterruptedException e) {
} catch (ExecutionException e) {
e.printStackTrace();
}
}
您在提交可调用对象后立即调用 future.get()
这一事实使您的代码有效地单线程化为 get
块,并且您不会在将来向池中提交更多任务已解决。在一个循环中提交所有内容并存储期货。然后迭代期货列表以收集结果。
这个更好:
List<Document> processDocs(String filePath) {
List<Callable<Document>> tasks = new ArrayList<>();
try {
BufferedReader br = new BufferedReader(IOUtils.fileReader(filePath));
String line = null;
while ((line = br.readLine()) != null) {
tasks.add(new NLPTextThread(line.trim());
}
ExecutorService executor = Executors.newfixedThreadPool(4);
return executor.invokeAll(tasks)
.stream()
.map(future -> {
try {
return future.get();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}).collect(Collectors.toList());
}
PS.
我想我也会 强调 评论者在原始问题上提出的 IO 问题。
如果您的 NLPTextThread
文件每一行的执行时间可以忽略不计(与从文件中读取这一行所花费的时间相比),我认为您不会看到 显着 改进 运行 使用线程池作为 IO 的时间在这种情况下实际上是一个瓶颈,因为您正在主线程(单线程)上读取单个大文件。如果将输入(如果很大)拆分为多个文件并并行处理 每个文件,您可能会看到更高的性能提升。仅供参考。