我有N个大文件(不少于250M)来散列。这些文件位于P物理驱动器上。
我想与最大K个活动线程同时散列它们但是我不能散列超过每个物理驱动器的M个文件,因为它会减慢整个过程(我运行测试,解析61个文件,并使用8个线程)比1个线程慢;文件几乎都在同一个磁盘上。
我想知道最好的方法是什么:
我的代码是:
int K = 8;
int M = 1;
Queue<Path> queue = null; // get the files to hash
final ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(K);
final ConcurrentMap<FileStore, Integer> counter = new ConcurrentHashMap<>();
final ConcurrentMap<FileStore, Integer> maxCounter = new ConcurrentHashMap<>();
for (FileStore store : FileSystems.getDefault().getFileStores()) {
counter.put(store, 0);
maxCounter.put(store, M);
}
List<Future<Result>> result = new ArrayList<>();
while (!queue.isEmpty()) {
final Path current = queue.poll();
final FileStore store = Files.getFileStore(current);
if (counter.get(store) < maxCounter.get(store)) {
result.add(newFixedThreadPool.submit(new Callable<Result>() {
@Override
public Entry<Path, String> call() throws Exception {
counter.put(store, counter.get(store) + 1);
String hash = null; // Hash the file
counter.put(store, counter.get(store) - 1);
return new Result(path, hash);
}
}));
} else queue.offer(current);
}
抛开潜在的非线程安全操作(比如我如何使用计数器),是否有更好的方法来实现我的目标?
我也认为这里的循环可能有点太多,因为它可能会占用很多进程(几乎就像一个无限循环)。
答案 0 :(得分:0)
如果驱动硬件配置在编译时是未知的,并且可能是chaged / upgrade,那么每个驱动器使用一个线程池并使用户可配置的线程计数很诱人。我不熟悉'newFixedThreadPool' - 线程数是否可以在运行时更改以优化性能?
答案 1 :(得分:0)
经过很长时间,我找到了一个解决方案来满足我的需求:我使用AtomicInteger
和每个提交的任务,而不是整数计数器,或ExecutorService
或其他任何东西。在一个驱动器的每个文件中共享Semaphore
。
像:
ConcurrentMap<FileStore, Semaphore> map = new ConcurrentHashMap<>();
ExecutorService es = Executors.newFixedThreadPool(10);
for (Path path : listFile()) {
final FileStore store = Files.getFileStore(path);
final Semaphore semaphore = map.computeIfAbsent(store, key -> new Semaphore(getAllocatedCredits(store)));
final int cost = computeCost(path);
es.submit(() -> {
semaphore.acquire(cost);
try {
... some work ...
} finally {
semaphore.release(cost);
}
});
}
int getAllocatedCredits(FileStore store) {return 2;}
int computeCost(Path path) {return 1;}
请注意Java 8的帮助,尤其是computeIfAbsent
和submit
。