添加一小部分我正在使用的文件(7 GB)并尝试运行该程序后,我可以看到以下内容:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/media/developer/golang/manual/examples/sp/v2/sp.v2.go:71 +0x4a9
exit status 2
我对GO完全陌生,如果我的问题很简单,对不起。
我正在尝试流化xml
文件,拆分文档,然后在不同的GO例程中解析它们。
我正在使用的XML文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="CGImap 0.0.2">
<relation id="56688" user="kmvar" uid="56190" visible="true" version="28" changeset="6947637" timestamp="2011-01-12T14:23:49Z">
<member type="node" ref="294942404" role=""/>
<member type="node" ref="364933006" role=""/>
<tag k="name" v="Küstenbus Linie 123"/>
<tag k="network" v="VVW"/>
<tag k="route" v="bus"/>
<tag k="type" v="route"/>
</relation>
<relation id="98367" user="jdifh" uid="92834" visible="true" version="28" changeset="6947637" timestamp="2011-01-12T14:23:49Z">
<member type="node" ref="294942404" role=""/>
<member type="way" ref="4579143" role=""/>
<member type="node" ref="249673494" role=""/>
<tag k="name" v="Küstenbus Linie 123"/>
<tag k="network" v="VVW"/>
<tag k="operator" v="Regionalverkehr Küste"/>
<tag k="ref" v="123"/>
</relation>
<relation id="72947" user="schsu" uid="92374" visible="true" version="28" changeset="6947637" timestamp="2011-01-12T14:23:49Z">
<member type="node" ref="294942404" role=""/>
<tag k="name" v="Küstenbus Linie 123"/>
<tag k="type" v="route"/>
</relation>
<relation id="93742" user="doiff" uid="61731" visible="true" version="28" changeset="6947637" timestamp="2011-01-12T14:23:49Z">
<member type="node" ref="294942404" role=""/>
<member type="node" ref="364933006" role=""/>
<tag k="route" v="bus"/>
<tag k="type" v="route"/>
</relation>
</osm>
我有以下代码段:
package main
import (
"encoding/xml"
"bufio"
"fmt"
"os"
"io"
)
type RS struct {
I string `xml:"id,attr"`
M []struct {
I string `xml:"ref,attr"`
T string `xml:"type,attr"`
R string `xml:"role,attr"`
} `xml:"member"`
T []struct {
K string `xml:"k,attr"`
V string `xml:"v,attr"`
} `xml:"tag"`
}
func main() {
p1D, err := os.Open("/media/developer/Transcend/osm/xml/relations.xml")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer p1D.Close()
reader := bufio.NewReader(p1D)
var count int32
var element string
channel := make(chan RS) // channel
for {
p2Rc, err := reader.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
} else {
fmt.Println(err)
os.Exit(1)
}
}
var p2Rs = string(p2Rc)
if p2Rc[2] == 114 {
count++
if (count != 1) {
go parseRelation(element, channel)
}
element = ""
element += p2Rs
} else {
element += p2Rs
}
}
for i := 0; i < 5973974; i++ {
fmt.Println(<- channel)
}
}
func parseRelation(p1E string, channel chan RS) {
var rs RS
xml.Unmarshal([]byte(p1E), &rs)
channel <- rs
}
应该打印每个结构,但我什么也看不到。该程序只是挂起。
我测试了流光和分离器(在向通道发送消息之前,刚在功能{strong> parseRelation 中添加了fmt.Println(rs)
)。我可以看到结构。因此,问题在于发送和接收消息。
我不知道如何解决这个问题。尝试更改通道中消息的类型(从RS
到string
),每次仅发送一个字符串。但这也没有帮助(我什么也看不到)
答案 0 :(得分:3)
首先,让我们解决这个问题:您不能逐行解析XML。您很幸运,您的文件碰巧是每行一个标签,但是这是理所当然的。您必须解析整个XML文档。
通过逐行处理,您试图将<tag>
和<member>
推入专为<relation>
设计的结构中。而是使用xml.NewDecoder
并让该文件为您处理文件。
package main
import (
"encoding/xml"
"fmt"
"os"
"log"
)
type Osm struct {
XMLName xml.Name `xml:"osm"`
Relations []Relation `xml:"relation"`
}
type Relation struct {
XMLName xml.Name `xml:"relation"`
ID string `xml:"id,attr"`
User string `xml:"user,attr"`
Uid string `xml:"uid,attr"`
Members []Member `xml:"member"`
Tags []Tag `xml:"tag"`
}
type Member struct {
XMLName xml.Name `xml:"member"`
Ref string `xml:"ref,attr"`
Type string `xml:"type,attr"`
Role string `xml:"role,attr"`
}
type Tag struct {
XMLName xml.Name `xml:"tag"`
Key string `xml:"k,attr"`
Value string `xml:"v,attr"`
}
func main() {
reader, err := os.Open("test.xml")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
decoder := xml.NewDecoder(reader)
osm := &Osm{}
err = decoder.Decode(&osm)
if err != nil {
log.Fatal(err)
}
fmt.Println(osm)
}
Osm
和其他结构类似于您期望的XML模式。 decoder.Decode(&osm)
应用该架构。
如果您只想提取XML的一部分,请see the answers to How to extract part of an XML file as a string?。
其余的答案将仅涉及通道和goroutine的使用。 XML部分将被删除。
如果添加一些调试语句,您会发现parseRelation
从未被调用,这意味着channel
是空的,而fmt.Println(<- channel)
却在等待一个永远不会关闭的空通道。因此,一旦处理完毕,请关闭频道。
for {
p2Rc, err := reader.ReadSlice('\n')
...
}
close(channel)
现在我们得到{ [] []}
5973974次。
for i := 0; i < 5973974; i++ {
fmt.Println(<- channel)
}
试图从该频道读取5973974次。这打败了渠道。而是read from the channel using range
。
for thing := range channel {
fmt.Println(thing)
}
现在至少完成了!
但是有一个新问题。如果它确实找到了东西,例如将if p2Rc[2] == 114 {
更改为if p2Rc[2] == 32 {
,您将得到一个panic: send on closed channel
。这是因为parseRelation
与读取器并行运行,并且可能在主要读取代码完成并关闭通道后尝试写入。您必须确保在使用该通道之前,每个人都已完成。
要解决此问题,需要进行相当大的重新设计。
这是一个简单程序的示例,该程序从文件中读取行,将其放入通道中,并从该通道中读取工作程序。
func main() {
reader, err := os.Open("test.xml")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Use the simpler bufio.Scanner
scanner := bufio.NewScanner(reader)
// A buffered channel for input
lines := make(chan string, 10)
// Work on the lines
go func() {
for line := range lines {
fmt.Println(line)
}
}()
// Read lines into the channel
for scanner.Scan() {
lines <- scanner.Text()
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
// When main exits, channels gracefully close.
}
这很好,因为main
是特殊的,并且在退出时清理频道。但是,如果读者和作家都是goroutine呢?
// A buffered channel for input
lines := make(chan string, 10)
// Work on the lines
go func() {
for line := range lines {
fmt.Println(line)
}
}()
// Read lines into the channel
go func() {
for scanner.Scan() {
lines <- scanner.Text()
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}()
空。 main
退出并关闭通道,然后goroutine才能完成工作。我们需要一种方法让main
知道等待处理完成。有两种方法可以做到这一点。一个是与another channel to synchronize processing在一起的。
// A buffered channel for input
lines := make(chan string, 10)
// A channel for main to wait for
done := make(chan bool, 1)
// Work on the lines
go func() {
for line := range lines {
fmt.Println(line)
}
// Indicate the worker is done
done <- true
}()
// Read lines into the channel
go func() {
// Explicitly close `lines` when we're done so the workers can finish
defer close(lines)
for scanner.Scan() {
lines <- scanner.Text()
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}()
// main will wait until there's something to read from `done`
<-done
现在main
将触发读取器和辅助程序,并缓冲等待done
上的内容。阅读器将填充lines
直到完成阅读,然后关闭它。并行地,工作人员将在完成阅读后从lines
读取并写入done
。
另一种选择是使用sync.WaitGroup。
// A buffered channel for input
lines := make(chan string, 10)
var wg sync.WaitGroup
// Note that there is one more thing to wait for
wg.Add(1)
go func() {
// Tell the WaitGroup we're done
defer wg.Done()
for line := range lines {
fmt.Println(line)
}
}()
// Read lines into the channel
go func() {
defer close(lines)
for scanner.Scan() {
lines <- scanner.Text()
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}()
// Wait until everything in the WaitGroup is done
wg.Wait()
和以前一样,main
启动读取器和工作程序goroutine,但是现在它在启动工作程序之前将1加到WaitGroup中。然后,它等待直到wg.Wait()
返回。阅读器的工作方式与之前相同,完成后lines
通道将关闭。现在,工作者在完成减少WaitGroup的计数并允许wg.Done()
返回后,调用wg.Wait()
。
每种技术都有优点和缺点。 done
更加灵活,链接更好,并且如果可以将其包裹住,则可以更安全。 WaitGroups更简单,更容易缠头,但是要求每个goroutine共享一个变量。
如果我们想添加到此处理链中,可以这样做。假设我们有一个goroutine可以读取这些行,一个可以在XML元素中对其进行处理,另一个可以对这些元素执行某些操作。
// A buffered channel for input
lines := make(chan []byte, 10)
elements := make(chan *RS)
var wg sync.WaitGroup
// Worker goroutine, turn lines into RS structs
wg.Add(1)
go func() {
defer wg.Done()
defer close(elements)
for line := range lines {
if line[2] == 32 {
fmt.Println("Begin")
fmt.Println(string(line))
fmt.Println("End")
rs := &RS{}
xml.Unmarshal(line, &rs)
elements <- rs
}
}
}()
// File reader
go func() {
defer close(lines)
for scanner.Scan() {
lines <- scanner.Bytes()
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}()
// Element reader
wg.Add(1)
go func() {
defer wg.Done()
for element := range elements {
fmt.Println(element)
}
}()
wg.Wait()
这会产生空的结构,因为您试图将XML的各行推入表示完整<relationship>
标签的结构中。但这说明了如何向链中添加更多工作人员。
答案 1 :(得分:0)
我不知道这是否对您有帮助,但是您的条件if p2Rc[2]==114
从未满足,然后您就可以开始收听频道了。永远不会收到输入。另外,还有更好的方法收听频道,例如select
,这里是一个示例https://tour.golang.org/concurrency/5。
我认为这里的主要问题是这个(代码)打算做什么?上述条件在哪里落入该过程?如果清楚的话,我可以做出更好的回应。