对于我们正在使用 go 的作业,我们要做的一件事是逐行解析 uniprotdatabasefile 以收集 uniprot 记录。
我不想共享太多代码,但我有一个工作代码片段可以在 48 秒内正确解析这样的文件(2.5 GB)(使用时间 go-package 测量)。它迭代地解析文件并向记录添加行,直到达到记录结束信号(完整记录),并创建记录上的元数据。然后将记录字符串清空,并逐行收集新记录。然后我想我会尝试使用 go-routines。
我之前从 stackoverflow 得到了一些提示,然后在原始代码中我简单地添加了一个函数来处理有关元数据创建的所有内容。
所以,代码正在做
- 创建一个空记录,
 - 迭代文件并向记录添加行,
 - 如果找到记录停止信号(现在我们有完整记录) - 将其交给 go 例程以创建元数据
 - 将记录字符串设为空并从 2) 继续。
 
我还添加了一个sync.WaitGroup()以确保我(最终)等待每个例程完成。我认为这实际上会减少解析数据库文件所花费的时间,因为它会继续解析,而 goroutine 会作用于每条记录。但是,代码似乎运行了 20 多分钟,这表明出现了问题或开销变得疯狂。有什么建议么?
package main
import (
    "bufio"
    "crypto/sha1"
    "fmt"
    "io"
    "log"
    "os"
    "strings"
    "sync"
    "time"
)
type producer struct {
    parser uniprot
}
type unit struct {
    tag string
}
type uniprot struct {
    filenames     []string
    recordUnits   chan unit
    recordStrings map[string]string
}
func main() {
    p := producer{parser: uniprot{}}
    p.parser.recordUnits = make(chan unit, 1000000)
    p.parser.recordStrings = make(map[string]string)
    p.parser.collectRecords(os.Args[1])
}
func (u *uniprot) collectRecords(name string) {
    fmt.Println("file to open ", name)
    t0 := time.Now()
    wg := new(sync.WaitGroup)
    record := []string{}
    file, err := os.Open(name)
    errorCheck(err)
    scanner := bufio.NewScanner(file)
    for scanner.Scan() { //Scan the file
        retText := scanner.Text()
        if strings.HasPrefix(retText, "//") {
            wg.Add(1)
            go u.handleRecord(record, wg)
            record = []string{}
        } else {
            record = append(record, retText)
        }
    }
    file.Close()
    wg.Wait()
    t1 := time.Now()
    fmt.Println(t1.Sub(t0))
}
func (u *uniprot) handleRecord(record []string, wg *sync.WaitGroup) {
    defer wg.Done()
    recString := strings.Join(record, "\n")
    t := hashfunc(recString)
    u.recordUnits <- unit{tag: t}
    u.recordStrings[t] = recString
}
func hashfunc(record string) (hashtag string) {
    hash := sha1.New()
    io.WriteString(hash, record)
    hashtag = string(hash.Sum(nil))
    return
}
func errorCheck(err error) {
    if err != nil {
        log.Fatal(err)
    }
}