【问题标题】:Concurrent reading of multiple files [closed]并发读取多个文件[关闭]
【发布时间】:2013-08-25 18:14:18
【问题描述】:

我正在处理我的网络服务器访问日志并将处理后的信息存储到我的数据库中。以前,我是单线程进程。完成这个过程花了很长时间。我决定使用并发文件读取来节省执行时间。我使用Executors 线程池实现了这一点。这是我的java代码。

日志文件处理程序

class FileHandler implements Runnable {

        private File file;

        public FileHandler(File file) {
            this.file = file;
        }

        @Override
        public void run() {
            try {
                byte[] readInputStream = readInputStream(new FileInputStream(file));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public static byte[] readInputStream(InputStream in) throws IOException {

            //closing the bytearrayoutput stream has no effect. @see java doc.
            ByteArrayOutputStream bos = null;
            byte[] buffer = new byte[1024];
            int bytesRead = -1;

            bytesRead = in.read(buffer);

            //no input to read.
            if(bytesRead == -1) {
                    return null;
            }

            bos = new ByteArrayOutputStream(in.available()); //creating output stream with approximate capacity.
            bos.write(buffer , 0 , bytesRead);

            try {
                while((bytesRead = in.read(buffer)) != -1) {
                    bos.write(buffer , 0 , bytesRead);
                }
            }finally {
                if(in != null) {
                    in.close();
                }
            }

            return bos.toByteArray();

         }


    }

并发文件读取

 public class AccessLogProcessor {

        public static void main(String[] args)  {

            String[] files = {

                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid1.txt" ,
                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid.txt"

            };

            long start = System.currentTimeMillis();

            ExecutorService executors = Executors.newFixedThreadPool(files.length);

            for(String file : files) {
                executors.execute(new FileHandler(new File(file)));
            }

            executors.shutdown();

            while(!executors.isTerminated());

            System.out.println("Time Taken by concurrent reading :: "+(System.currentTimeMillis()-start) + " ms ");

        }

}

单线程文件读取

    public class Test {

        public static void main(String[] args) throws FileNotFoundException, IOException  {

            String[] files = {

                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid1.txt" ,
                    "/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid.txt"

            };

            long start = System.currentTimeMillis();

            for(String file : files) {
                FileHandler.readInputStream(new FileInputStream(file));
            }

            System.out.println("Time Taken by concurrent reading :: "+(System.currentTimeMillis()-start) + " ms ");

        }

}

10轮执行的测试结果

单线程执行:9ms。

并发执行:14 毫秒。

我正在同时读取文件,但为什么耗时大于单线程执行?请纠正我如果我做错了什么?

【问题讨论】:

  • 查找上下文切换和I/O资源争用;使用单个物理磁盘,并发访问总是会减慢 I/O
  • @JarrodRoberson ,您能否简要解释一下以使我摆脱困境?
  • “查找context switching and contention of I/O resources”的哪一部分你不明白?谷歌那个确切的短语。

标签: java multithreading io


【解决方案1】:

我可以看到几个问题:

  1. 您的测试文件似乎非常小,并且将完全缓存在 RAM 中,因此您的基准测试没有模拟真正的问题,因为它没有考虑 I/O 时间。此外,由于它是如此之小,您所做的任何改进都可能会被管理线程的开销所吸收。

  2. 除非您所做的数据处理工作相当复杂,否则它主要是 I/O 密集型任务,而不是 CPU 密集型任务,并且除非您的文件都在不同的磁盘上,否则需要同时处理一个磁盘对不同数据的请求不会使其工作得更快。事实上,对于传统硬盘而不是固态存储,像这样的“并发”I/O 可能会慢得多,因为驱动器头 (the pointy arm thing) 会疯狂地来回滑动以到达不同的部分磁盘。

  3. System.currentTimeMillis() 在大多数系统上的准确度为 +/- 10 毫秒。因此,您 9 毫秒和 14 毫秒的基准测试结果实际上并不能告诉您任何事情。使用System.nanoTime() 获得更准确的计时器。

  4. 您运行了多少次代码?显然每次运行程序时只有一次。如果它少于数千次,那么您根本没有测量任何有用的东西,因为代码仍在编译中。不要难过:每个人都会犯这个错误!动态编译语言中的基准测试太难了。要了解如何编写有用的基准,请阅读:

    至少,您应该在循环中多次运行您的完整代码,以便在 内完成,而不是几毫秒。

  5. 在频率切换 CPU(即现代 CPU)上,CPU 在空闲时以低频运行。只有当它开始工作时,它才会逐渐提高速度,这可能会混淆基准测试,除非您将其设置为固定速度(最大电池或最大性能)模式。或者,修复问题 4 将修复问题 5,因为使用长循环或添加预热代码会吸收 CPU 频率切换的影响。

简短的回答是您当前的基准太小无法给出有意义的结果。一旦您运行了更准确地模拟实际问题的更长基准测试,您将能够知道多线程是否使其更快。

【讨论】:

  • 我喜欢你的回答。它说明了我的回答所做的一切(除了添加“磁盘缓存”额外点)。更令人惊讶的是为什么它没有受到白痴公司的攻击。你很酷。
【解决方案2】:

首先,您应该了解并行性适用于处理器执行的一系列指令。处理器执行一系列指令。虚拟处理器称为threads。您可以分配多个线程来并行执行多条指令。但是,这并不意味着多处理允许您通过网络连接更快地下载电影,从而增加带宽。这意味着您的并行线程将暂停等待网络数据到达。好的?我们应该对不理解这个简单事情的人做点什么。

另外,我必须补充一点,10 ms 是 java 中的时间测量误差。因此,您的读数是时间测量噪声。基准测试需要一些时间让系统升温并需要更长的时间间隔,以便计时器分辨率不会引入重大错误。

【讨论】:

  • 现在,我明白了。谢谢。
  • 这在许多方面都是错误的,这很可悲。
  • 首先从根本上是错误的,因为它会使整个答案的其余部分也错误。 “虚拟处理器被称为线程”
  • 首先,如果你认为这句话是错误的,你不知道什么是Executor(线程池)。线程池 = 处理器池。其次,我正在等待听到其他因此而“错误”的战线。
  • 谢谢,帽子。它还说这些线程执行提交给它们执行的代码。你如何称呼那些执行你的代码的物理实体?用软件模拟出来的硬件怎么称呼?
猜你喜欢
  • 1970-01-01
  • 2013-08-20
  • 2017-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-29
  • 2019-02-10
相关资源
最近更新 更多