我在八个线程中拆分计算并将结果写入文件,如下所示:
1a上。七个线程中的每一个都处理其输入并将其输出写入其自己的ByteArrayOutputStream
;当流关闭时,线程offers
和<Integer, ByteArrayOutputStream>
到ConcurrentLinkedQueue
,并在countDown()
上调用CountDownLatch
(已初始化为7)。
1b中。同时,第八个线程读入将在下一次迭代中处理的所有输入数据。当awaits
完成对其数据的读取时,此帖子CountDownLatch
。
2a上。当CountDownLatch
达到0时,第八个线程唤醒,使用ConcurrentinkedQueue
中的Integer
作为排序键对<Integer, ByteArrayOutputStream>
进行排序,然后遍历队列并附加字节数组到文件。 (可能有一种更有效的方式来按顺序遍历列表而不对其进行排序,但列表中只有七个元素,因此排序方法的运行时不是问题。)
2B。同时,其他七个线程处理由第八个线程为它们准备的输入。
**此过程循环,直到处理完所有数据(通常为40-80次迭代)。
每个线程处理8mb的大小相等的输入块(可能在最后一次迭代时除外);每个ByteArrayOutputStream
包含1-4 MB,并且输出大小不能提前知道。通常,最早完成和最新完成的CPU绑定线程的运行时间在彼此的20%之内。
我想知道是否有一个IO库(或者我已经错过的java.io或java.nio中的方法)已经做了类似的事情 - 目前第八个线程(IO线程)是大约75%的时间都处于空闲状态,但是我提出的任何方式来缓解这种低效率都会让我觉得太复杂(因此在创建死锁或数据争用方面风险太大);例如,我可以将输入分成4 mb块,然后给七个CPU绑定线程提供两个块,并将一个块提供给IO绑定线程,这理论上将IO线程的空闲时间减少到25% (25%的IO,50%的4 MB大块,25%的空闲),但这是一个脆弱的解决方案,可能无法移植到另一个CPU(这意味着在另一个CPU上,IO绑定的线程可能会变成瓶颈,如果例如,它的运行时间是CPU绑定线程的150%) - 我真的很喜欢自平衡解决方案,因此我不需要手动微调负载平衡。
答案 0 :(得分:1)
低效率包括在线程8处理任何输出之前等待所有7个输出完成。最好运行7个队列而不是一个队列,即每个源线程一个队列,并按必要的顺序读取它们。这样,当第一个队列有任何数据时,它会被立即处理,而不必等待另一个6;类似的队列2..6。当线程8完成最后一个队列时,它可以开始生成,或者它确实可以这样做而不是等待任何特定队列开始生成。
答案 1 :(得分:0)
我修改了算法如下:
BlockingQueue
中,CPU线程从poll/take
处理其工作块ConcurrentSkipListMap<Integer, ByteArrayOutputStream>
ConcurrentSkipListMap
(使用firstKey
)以查看是否有任何要写入的数据(我维护一个计数器,指示下一个键应该是什么,以确保输出流被写入然后它检查BlockingQueue
的长度以查看是否需要向其添加任何数据(如果queue.size() < N
然后我向其添加N个更多块,其中N最初等于12);如果它执行了其中一个或两个IO任务,那么它就会循环,否则它会处理BlockingQueue
中的一个块,然后循环。除非整个输入已被IO线程处理,否则BlockingQueue
不应为空 - 空队列表示需要提高queue.size() < N
阈值。因此,CPU线程的逻辑是
while(!cancel) {
try {
Input input = queue.poll();
if(input == null) {
log.warn("Empty queue");
input = queue.take();
}
process(input);
} catch (InterruptedException ex) {
cancel = true;
}
}