并行写入Java中的多个文件

时间:2016-07-10 18:31:01

标签: multithreading java-8

我有什么

我有很多CSV文件,如下所示:

const regKey = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`

func getSettingsFromRegistry() (settings map[string]string) {
    settings = make(map[string]string)

    k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKey, registry.QUERY_VALUE)
    if err != nil {
        self.Log.Println("Can't open registry key " + regKey , err)
        return
    }
    defer k.Close()

    params, err := k.ReadValueNames(0)
    if err != nil {
        self.Log.Printf("Can't ReadSubKeyNames %s %#v", psaConfRegistry32, err)
        return
    }

    for _, param := range params {
        val, err := getRegistryValueAsString(k, param)
        if err != nil {
            self.Log.Println(err)
            return
        }
        settings[param] = val
    }

    self.Log.Printf("%#v\n", settings)
    return
}

func getRegistryValueAsString(key registry.Key, subKey string) (string, error) {
    valString, _, err := key.GetStringValue(subKey)
    if err == nil {
        return valString, nil
    }
    valStrings, _, err := key.GetStringsValue(subKey)
    if err == nil {
        return strings.Join(valStrings, "\n"), nil
    }
    valBinary, _, err := key.GetBinaryValue(subKey)
    if err == nil {
        return string(valBinary), nil
    }
    valInteger, _, err := key.GetIntegerValue(subKey)
    if err == nil {
        return strconv.FormatUint(valInteger, 10), nil
    }

    return "", errors.New("Can't get type for sub key " + subKey)
}

第二行不是错误 - 日期是“随机”,在2014年和2016年之间。幸运大多数日期与最后两行类似。但上面的序列就是一个真实的例子。

文件怎么样以及为什么要并行?

每年有5000个文件。每一个都是gziped。所以IO不是问题。 CPU现在很无聊。

我需要什么

来自上述文件的行在分隔文件中按天分组。我不在乎里面的订单。

我在想什么

使用Java并行流来读取文件。但我不知道如何将线程安全写入多个文件? 我找到了类似的内容:Write to text file from multiple threads?Threads and file writing但我不确定这是不是一种方法?

2 个答案:

答案 0 :(得分:0)

有很多可能性:

  • 线程池+ IO方法

线程池。每个线程读取一个输入文件并写入多个输出文件(取决于输出文件的数量 - 也许您可以打开所有输出文件,可能只有一些输出文件由于许多打开文件的限制)< / p>

  • 2x线程池+ Map<Int, ArrayBlockingQueue> + IO方法

用于读取文件的线程池。每个线程解析数据并将结果放入ArrayBlockingQueue实例,该实例在日期使用哈希函数找到。对于每个ArrayBlockingQueue,都有一个负责写入输出文件的线程。

  • 一个线程+ NIO方法(非阻塞)

一个线程从众多输入文件中读取并使用NIO写入多个输出文件。

  • 上述
  • 的混合物

所选择的解决方案取决于许多因素:

  • 此功能是否是您应用程序的性能瓶颈?
  • 最简单的单线程IO解决方案太慢了吗?
  • 您希望/可以投入多少时间
  • 您的操作系统和硬盘

答案 1 :(得分:0)

我的直觉不是要过度思考这些事情。我的方法是:

  1. 每个输入文件在池化线程上逐行处理。当一个 遇到新的日期,打开一个新的文件编写器并将其放入 地图(日期是关键)。
  2. 将您刚刚阅读的一行写入相应的作者(访问 写作者,写自己并在地图中添加一个条目必须是 当然是同步的)
  3. 设置屏障以在所有处理后立即关闭所有输出文件 线程完成。
  4. 虽然易于实施和调试,但这种方法不是防弹的,也不提供最佳性能。 缺点是:

    1. 写入文件时的争用
    2. 争论改变文件编写者的地图
    3. 耗尽打开的文件描述符(我们为每个人保留一个文件编写器 日期)
    4. 如果性能是主要问题,我会提倡map-reduce方法,其中每个文件处理线程产生一些日期隔离文件,然后另一个进程连接这些文件。