【问题标题】:Golang and Concurrency/ParallelismGolang 和并发/并行
【发布时间】:2015-07-07 01:17:19
【问题描述】:

我正在深入研究 Golang,遇到一个问题,我已经研究了几天,但我似乎无法掌握 Go 例程的概念以及它们的使用方式。

基本上,我正在尝试生成数百万条随机记录。我有生成随机数据的函数,并将创建一个包含这些数据的巨大 .CSV 文件。

我的问题是是否有可能使这个并发并加快速度?

我的代码基本上是生成一个随机字符串,将字符串写入文件最多 N 次(其中 N 是你想要的任何东西)。

我的问题是是否可以同时执行此操作以减少执行时间。似乎无论我如何处理这个问题,我仍然得到相同的基准,就好像我没有使用 goroutine 一样。

这是我目前所拥有的示例:

func worker(c chan string) {
        for {
                c <- /* Generate random data using other functions here */
        }
        close(c)
}

func writer(s string) {
        csvfile.WriteString(s)
}

func main(){    
    receive := make(chan string)

    for i := 0; i < 100; i++ {
        go worker(receive)
    }

    for i := 0; i < 10000; i++ {
        go writer(<-receive)
    }
}

在我生成数据的地方,我使用了大量来自:https://github.com/Pallinder/go-randomdata 的函数调用。你认为这可能是我一直输的地方吗?

任何帮助将不胜感激。

【问题讨论】:

  • 请注意,默认的随机数生成器对于并发使用是安全的,它通过锁定来实现。如果您在 goroutine 中使用大量随机数,您希望通过为每个 goroutine 设置一个隔离的 *math.Rand 来避免它们序列化对随机数的访问。你可以通过rand.New(和rand.NewSource,每个课程使用不同的种子)来做到这一点。
  • 切向相关:加快文件写入的最快方法是使用一个大 fwrite 一次写入所有文件,如果您需要更快,那么您可以考虑关闭处理器中的内部缓冲缓存和硬盘缓存,我认为这通常取决于系统。如果你真的很详细,你可能需要一种较低级别的语言,比如 C

标签: go


【解决方案1】:

我认为您不应该在这里尝试使用 go 例程。文件写入几乎总是原子的,你想让写入文件的机制并发......这将需要一个复杂的锁定机制,由于写入本身仍然是原子的,最终可能不会提高应用程序的性能。

如果数据生成阻碍了您的程序,那么将这项工作拆分到 go 例程中并从您获取所有数据的地方写入是有意义的。但是

for i := 0; i < 100; i++ {
    go worker(receive)
}

for {
    select {
    case item := <-receive:
         writer(item)
    case <-abort:
         cleanUp()
         return
    }
}

您不能在从通道接收并无休止地调用函数的同时循环某个 int ......不过,您可以在选择中从通道接收。或者只是通过执行item := &lt;-recieve 这将阻塞直到一个项目被读取。在上面的示例中,我提供了一些伪代码来更多地演示在这种情况下您的设计应该是什么。您需要一个中止通道,以便在您想要停止应用程序时退出您的 goroutine。它可能应该完成对文件的写入,然后在返回之前将其关闭。

【讨论】:

  • 好吧,我一直认为这会在您进行写入时阻碍性能。基本上你的意思是,无论你生成数据的速度有多快,除非我们有某种“完成”条件,否则写入总是一样的?
  • @art_vandelay 我是说操作系统和硬盘驱动器将决定您写入单个文件的速度,而不是您的应用程序的并发级别。如果您写入一堆文件并将它们聚合为一个(假设您现在正在处理大量数据),那么它也许可以节省您的时间。我的意思是,如果生成数据比写入慢,您可以同时生成数据以提高性能。由于您的写入本质上不是并发的,因此您不能只是尝试从一堆不同的 go 例程中写入来提高性能。
  • 哇,这很有道理哈哈。好的,有了这个更好的理解,我将尝试重新编写这个坏男孩。感谢您的意见。
  • @art_vandelay 很高兴我能帮上忙!在这种情况下,在它自己的 go 例程中进行每次写入是非常有意义的。您仍然可能希望使用与我上面的模型类似的模型,因为制作响应式应用程序在某种程度上是必要的,但是当您阅读时,您可以调用一个方法在 goroutine 中进行写入并将项目传递给它,以便继续执行和下一个文件的数据一可用就开始写入。
  • @art_vandelay 我建议使用循环变量在 for 循环中播种,以确保每次迭代都有不同的值并将其传递给 worker 方法。即func Seed(seed int64)i 作为arg,然后将arg 添加到worker 并使用rand.Uint32() 或64 或您希望随机值的任何类型调用它。
【解决方案2】:

试试buffering your channel:

receive := make(chan string, 1000)

写入速度受磁盘限制,因此您只能通过并发写入来提供帮助,而根据您所说的并发生成数据也无济于事。

并发不是任何缓慢的解决方案,要么接受你的极限或优化。

【讨论】:

    猜你喜欢
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多