【问题标题】:Can multiple threads write data into a file at the same time?多个线程可以同时将数据写入文件吗?
【发布时间】:2012-01-26 00:16:24
【问题描述】:

如果你曾经使用过 p2p 下载软件,他们可以通过多线程下载一个文件,而且他们只创建一个文件,所以我想知道线程是如何将数据写入该文件的。顺序还是并行?

想象一下你想将一个大的数据库表转储到一个文件中,如何让这个工作更快?

【问题讨论】:

  • 是的,他们可以,但你真的想避免他们在同一时间这样做。需要进行一些协调。如果您在谈论 Bittorrent,我认为这是通过将已知偏移量的块组装到已知大小的文件中来实现的。那不能顺序或并行调用,它更像是“随机访问”。

标签: java multithreading file-io concurrency


【解决方案1】:

您可以使用多个线程将 a 写入文件,例如一个日志文件。但是正如@Thilo 指出的那样,您必须协调您的线程。您需要同步文件访问并且只写入整个记录/行,或者您需要制定将文件区域分配给不同线程的策略,例如重新构建一个已知偏移量和大小的文件。

出于性能原因,很少这样做,因为大多数磁盘子系统在顺序写入时性能最佳,而磁盘 IO 是瓶颈。如果 CPU 创建记录或文本行(或网络 IO)是瓶颈,它可以提供帮助。

您想将大数据库表转储到文件的图像,以及如何使这项工作更快?

按顺序写可能是最快的。

【讨论】:

  • 使用信号量
【解决方案2】:

Java nio 包被设计为允许这样做。以http://docs.oracle.com/javase/1.5.0/docs/api/java/nio/channels/FileChannel.html 为例。

您可以将一个文件的多个区域映射到不同的缓冲区,每个缓冲区可以由单独的线程单独填充。

【讨论】:

  • +1 如果想用 Java 实现这个“p2p 下载软件”,这似乎是一个很好的 API。
【解决方案3】:

同步声明可以做到这一点。试试下面我在类似上下文中使用的代码。

package hrblib;

import java.io.*;

public class FileOp {

    static int nStatsCount = 0;

    static public String getContents(String sFileName) {  

        try {
            BufferedReader oReader = new BufferedReader(new FileReader(sFileName));
            String sLine, sContent = "";
            while ((sLine=oReader.readLine()) != null) {
                sContent += (sContent=="")?sLine: ("\r\n"+sLine);
            }
            oReader.close();
            return sContent;
        }
        catch (IOException oException) {
            throw new IllegalArgumentException("Invalid file path/File cannot be read: \n" + sFileName);
        }
    }
    static public void setContents(String sFileName, String sContent) {
        try {
            File oFile = new  File(sFileName);
            if (!oFile.exists()) {
                oFile.createNewFile();
            }
            if (oFile.canWrite()) {
                BufferedWriter oWriter = new BufferedWriter(new FileWriter(sFileName));
                oWriter.write (sContent);
                oWriter.close();
            }
        }
        catch (IOException oException) {
            throw new IllegalArgumentException("Invalid folder path/File cannot be written: \n" + sFileName);
        }
    }
    public static synchronized void appendContents(String sFileName, String sContent) {
        try {

            File oFile = new File(sFileName);
            if (!oFile.exists()) {
                oFile.createNewFile();
            }
            if (oFile.canWrite()) {
                BufferedWriter oWriter = new BufferedWriter(new FileWriter(sFileName, true));
                oWriter.write (sContent);
                oWriter.close();
            }

        }
        catch (IOException oException) {
            throw new IllegalArgumentException("Error appending/File cannot be written: \n" + sFileName);
        }
    }
}

【讨论】:

    【解决方案4】:

    您可以让多个线程写入同一个文件 - 但一次一个。在写入文件之前,所有线程都需要进入一个同步块。

    在 P2P 示例中 - 实现它的一种方法是找到文件的大小并创建一个该大小的空文件。每个线程都在下载文件的不同部分——当它们需要写入时,它们将进入一个同步块——使用 seek 移动文件指针并写入缓冲区的内容。

    【讨论】:

    • "所有线程在写入文件之前都需要进入一个同步块。"。好吧,他们没有不得不。但是你不会喜欢这样的输出,它可能会以有趣的方式交错。
    【解决方案5】:

    这是什么文件?为什么你需要用更多的线程喂它?这取决于文件使用的特征(我不知道更好的词)。

    通过网络从多个地方传输文件(简称:类似 Torrent)

    如果您正在传输现有文件,程序应该

    • 一旦知道文件的大小,就用空内容创建它:这样可以防止以后出现磁盘外错误(如果没有足够的空间,它会在创建时出现,然后再下载任何它),也有助于提高性能;
    • 如果你组织好传输(为什么不组织好),每个线程将负责文件的不同部分,因此文件写入将是不同的,
    • 即使两个线程以某种方式选择文件的同一部分,也不会导致错误,因为它们为相同的文件位置写入相同的数据。

    将数据块附加到文件(简称:日志记录)

    如果线程只是将固定或各种长度的信息附加到文件中,您应该使用公共线程。它应该使用相对较大的写入缓冲区,因此它可以快速为客户端线程提供服务(仅获取字符串),并将其刷新出最佳调度和块大小。它应该使用专用磁盘甚至计算机。

    此外,可能存在多个性能问题,这就是为什么存在日志服务器,甚至是昂贵的商业服务器。

    读写随机时间、随机位置(简称:数据库)

    它需要复杂的设计,包括互斥锁等,我从来没有做过这种事情,但我可以想象。向 Oracle 询问一些技巧 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-17
      • 1970-01-01
      • 2020-04-14
      • 2017-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多