【问题标题】:Optimal way to read a file concurrently并发读取文件的最佳方式
【发布时间】:2017-10-14 13:02:13
【问题描述】:

所以我有一个这样的文件:

NAME : a280
COMMENT : drilling problem (Ludwig)
TYPE : TSP
DIMENSION: 280
EDGE_WEIGHT_TYPE : EUC_2D
NODE_COORD_SECTION
  1 288 149
  2 288 129
  3 270 133
  4 256 141
  5 256 157
  6 246 157
  7 236 169
  8 228 169
  9 228 161
 10 220 169
 11 212 169
 12 204 169
 13 196 169
 14 188 169
 15 196 161

等等……

数字是城市解决 TSP 的绳索。我正在尝试用 Golang 写这个。现在实例可以像 200 个城市,甚至 40.000 个城市。我想得到最好的解决方案,所以我想我应该同时处理这个文件。我有以下代码:

package main

import (
    "bufio"
    "fmt"
    "os"
    "regexp"
    "strings"
)

func getCords(queue chan string) {
    cords := regexp.MustCompile(`\s*\d+\s+\d+\s+\d+`)
    for line := range queue {
        if cords.MatchString(line) {
            fmt.Println(line)
        }
    }
}

func readFile(fileName string) {
    cords := make(chan string)
    file := strings.NewReader(fileName)
    go func() {
        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            cords <- scanner.Text()
        }
        close(cords)
    }()
}

// Menu - main program menu
func Menu() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Println("==================    Projektowanie efektywnych algorytmów    ==================")
    fmt.Println("==================    Zadanie nr 1 - Algorytm xyz             ==================")
    // Wczytywanie pliku z danymi
    // Format: Lp. X Y
    fmt.Printf("\nPodaj nazwę pliku: ")
    fileName, err := reader.ReadString('\n')
    if err != nil {
        fmt.Println(err)
        return
    }
    readFile(fileName)
}

func main() {
    Menu()
}

getCords函数中,我需要使用正则表达式,因为文件开头往往有信息部分。

问题始于readFile()。我启动了一个 goroutine,它逐行扫描文件并将所有行获取到通道。当然,执行只是启动它并继续前进。现在的问题是,在go func() 调用之后,我将不得不尝试从频道读取。我在 SO 和互联网上找到的解决方案如下:

func readFile(fileName string) {
    cords := make(chan string)
    file := strings.NewReader(fileName)
    go func() {
        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            cords <- scanner.Text()
        }
        close(cords)
    }()
    for i := 0; i < 100; i++ {
        go getCords(cords)
    }
}

因此,第一次执行 getCords 可能甚至什么都不做,因为 goroutine 无法以那么快的速度将线路连接到通道。下一次迭代可能会完成这项工作,但问题是我必须写一些数字,比如这个例子中的100,它可能太高了,所以通道将在 10 次迭代后关闭,之后它只是一个浪费时间或者它可能太低,然后我就不会得到所有的结果。

你们如何解决这样的问题,伙计们?有没有最佳方法,还是我必须坚持一些waitGroups

【问题讨论】:

  • 首先编写一个可以工作的顺序版本,然后,使用分析找出程序中最慢的部分,最后开始在相关的地方为程序添加并发性。此外,github.com/AkshanshChahal/TSP-Golang
  • 您没有在代码中的任何位置打开任何文件。 file := strings.NewReader(fileName) 不会打开并从 fileName 加载数据。 os.Open 这样做:golang.org/pkg/os/#Open.

标签: go concurrency


【解决方案1】:

我认为是的,最好使用sync.WaitGroup 来确保所有 goroutine 都完成了它们的工作。一种可能的解决方案:

func getCords(queue Chas string, wg sync.WaitGroup) {
     defer wg.Done()
     // your code 
 }

func readFile(fileName string) {
    cords := make(chan string)
    file := strings.NewReader(fileName)

    go func() {
        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            cords <- scanner.Text()
        }
        close(cords)
    }()

    wg := sync.WaitGroup{}
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go getCords(cords, wg)
    }
    wg.Wait()
}

【讨论】:

  • 但是一个 goroutine 完成了对通道的所有读取。所以这个 goroutine 开始了,执行更进一步。那我应该在哪里打电话go getCords()
  • 我已经更新了代码。这不是问题 - 因为写入 cords &lt;- scanner.Text() 将被阻止。因为在getCoords goroutine 启动之前,没有人可以从通道中读取。您也可以交换这两个块:在启动消费者 getCoords 之前和之后启动生产者 - 文件阅读器。
  • 对,忘记发送块 goroutine 直到有人从频道读取。我不必通过指针传递wg 吗?当我们不知道它会读取多少行时,为什么要为 100 迭代执行 for 循环?
  • 顺便说一句,您的代码不能以两种方式工作。首先,getCords 不接受 sync.WaitGroup 按值传递,其次当我更改它时,程序执行结束时没有结果(就像我将 fmt.Println 添加到 getCords 时一样)。 F.e 它只是打印了文件名
  • 当然wg应该是通过ponter的。作为上述评论者,还应该有Open。不幸的是,我稍后会在使用笔记本电脑时检查它,而不是手机。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-26
相关资源
最近更新 更多