1

这是一篇很长的帖子,所以我不得不感谢您的阅读。我的应用程序应该处理很多声音文件,比如说 4000+。我的第一种方法是加载一定数量(比如说 200mb)的声音数据,对其进行处理、写入,然后“清空”数据以让 gc 释放它。但考虑到数据是通过 Intranet 加载的,这似乎不是“最好”的方式。(文件访问很慢)计算应该从第一个加载的文件开始。为了实现这一点,我将概念更改为一种“生产者/消费者”(我认为)。到目前为止,这是我的课程:

读者/制片人

public class ReaderThread extends Thread {

    List<Long> files;
    ConcurrentLinkedQueue<Long> loaded = new ConcurrentLinkedQueue<Long>();
    boolean finished = false;

    public ReaderThread( List<Long> soundFiles) {
        this.files = soundFiles;
    }

    @Override
    public void run() {
        Iterator<Long> it = files.iterator();
        while(it.hasNext()) {
            Long id = it.next();
            if (FileLoader.load(id)) {
                loaded.add(id);
            }
        }
        finished = true;
    }

    public Long getNextId() {
        while(loaded.isEmpty()) {
            if( finished ) {
                return null;
            }
        }
        Long id = loaded.poll();
        return id;
    }
}

这是作者/(不是消费者)

public class WriterThread extends Thread {

    ConcurrentLinkedQueue<Long> loaded = new ConcurrentLinkedQueue<Long>();
    String directory;
    boolean abort = false;

    public WriterThread(String directory) {
        this.directory = directory;
    }

    @Override
    public void run() {

        while(!(abort&&loaded.isEmpty())) {
            if(!loaded.isEmpty()) {
                Long id = loaded.poll();
                FileWriter.write(id, directory);
                FileManager.unload(id);
            }
        }
    }

    public synchronized void submit(Long id) {
        loaded.add(id);
    }

    public synchronized void halt() {
        abort = true;
    }

}

这是所有东西聚集在一起的部分:

    // Forgive me the "t" and "w". ;-)
                    t = new ReaderThread(soundSystem,soundfilesToProcess);
            w = new WriterThread(soundSystem,outputDirectory );

            t.start();
            w.start();

            long start = System.currentTimeMillis();
            while(!abort) {

                Long id = t.getNextId();
                if(id!=null) {

                    SoundFile soundFile = soundSystem.getSoundfile(id);
                    ProcessorChain pc = new ProcessorChain(soundFile, getProcessingChain(), w);
                        Future<List<ProcessorResult>> result = es.submit(pc);
                        results.add(result);

                }else {
                    break;
                }
            }
for( Future<List<ProcessorResult>> result : results) {
            List<ProcessorResult> tempResults;
            try {
                tempResults = result.get();
                processResults(tempResults);

            } catch (InterruptedException e) {

                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

        }   
w.halt();

“ProcessorChain”是一个可运行的。es.submit -> "es" 是一个 CachedThreadPool。

我首先需要知道的是天气与否,这种方法是否“好”,或者它是否更像是“废话”。它似乎工作得很好,但我对编写器线程几乎没有问题,似乎在某些情况下并非所有文件都被写入。编写器线程提交方法由处理器链完成工作后调用。第二件事是线程安全。我错过了什么吗?

4

2 回答 2

3

我相信如果每个线程读取、处理然后写入整个声音文件(每个文件一个线程),它会(很多)简单。

您可以使用 Java线程池,并让操作系统/Java VM 将多个文件的读/处理/写并行化以提高效率。我可能是错的,但是根据您描述的更简单的解决方案就足够了,然后如果需要进一步改进,您可以衡量您的瓶颈。

于 2010-07-21T21:12:23.820 回答
1

我认为这种方法总体上是可以的(一个线程用于读取输入,一个线程用于写入输出,一个或多个线程用于处理)。

几个建议:

1 - 你可能想使用信号量而不是让你的线程在一个恒定的循环中旋转。例如,使用信号量,您的写入线程只会阻塞,直到文件实际可写入。目前它会旋转,当没有什么可写的时候,可能会浪费你 1/3 的 cpu 周期。

2 - 您可能想要显式创建工作线程而不是在主线程上进行工作。这样你就可以让多个线程同时进行处理。这可能已经是 ProcessorChain 正在做的事情,但从片段中并不清楚。

于 2010-07-21T21:36:34.930 回答