Golang致命错误:并发映射读取和映射写入

时间:2017-08-09 08:36:37

标签: dictionary go

我在Go中编写minecraft服务器,当服务器受到2000多个连接的压力时,我遇到了这个崩溃:

致命错误:并发映射读取和映射写入/ root / work / src / github.com / user / imoobler / limbo.go:78 + 0x351 由main.main /root/work/src/github.com/user/imoobler/limbo.go:33 + 0x368创建

我的代码:

package main

import (
    "log"
    "net"
    "bufio"
    "time"
    "math/rand"
    "fmt"
)

var (
    connCounter = 0
)

func main() {
    InitConfig()
    InitPackets()

    port := int(config["port"].(float64))
    ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Server launched on port", port)
    go KeepAlive()
    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Print(err)
        } else {
            connCounter+=1
            go HandleConnection(conn, connCounter)
        }
    }
}

func KeepAlive() {
    r := rand.New(rand.NewSource(15768735131534))
    keepalive := &PacketPlayKeepAlive{
        id: 0,
    }
    for {
        for _, player := range players {
            if player.state == PLAY {
                id := int(r.Uint32())
                keepalive.id = id
                player.keepalive = id
                player.WritePacket(keepalive)
            }
        }
        time.Sleep(20000000000)
    }
}

func HandleConnection(conn net.Conn, id int) {
    log.Printf("%s connected.", conn.RemoteAddr().String())

    player := &Player {
        id: id,
        conn: conn,
        state: HANDSHAKING,
        protocol: V1_10,
        io: &ConnReadWrite{
            rdr: bufio.NewReader(conn),
            wtr: bufio.NewWriter(conn),
        },
        inaddr: InAddr{
            "",
            0,
        },
        name: "",
        uuid: "d979912c-bb24-4f23-a6ac-c32985a1e5d3",
        keepalive: 0,
    }

    for {
        packet, err := player.ReadPacket()
        if err != nil {
            break
        }

        CallEvent("packetReceived", packet)
    }

    player.unregister()
    conn.Close()
    log.Printf("%s disconnected.", conn.RemoteAddr().String())
}

现在服务器只是" limbo"。

1 个答案:

答案 0 :(得分:11)

一般来说(无法访问发生错误的代码),您有几个选择。以下是其中两个:

sync.RWMutex

使用sync.RWMutex{}控制对地图的访问权限。如果您具有单个读取和写入,请使用此选项,而不是地图上的循环。见RWMutex

这是一个通过someMap someMapMutexvar ( someMap = map[string]string{} someMapMutex = sync.RWMutex{} ) go func() { someMapMutex.Lock() someMap["key"] = "value" someMapMutex.Unlock() }() someMapMutex.RLock() v, ok := someMap["key"] someMapMutex.RUnlock() if !ok { fmt.Println("key missing") return } fmt.Println(v) 进行访问控制的示例:

syncmap.Map{}

syncmap.Map

使用map代替普通syncmap.Map{}。此地图已经处理了种族问题,但根据您的使用情况可能会变慢。 var ( someMap = syncmap.Map{} ) go func() { someMap.Store("key", "value") }() v, ok := someMap.Load("key") if !ok { fmt.Println("key missing") return } fmt.Println(v) // with syncmap, looping over all keys is simple without locking the whole map for the entire loop someMap.Range(func(key, value interface{}) bool { // cast value to correct format val, ok := value.(string) if !ok { // this will break iteration return false } // do something with key/value fmt.Println(key, val) // this will continue iterating return true }) 的主要优势在于for循环。见syncmap

-race

一般建议

您应该使用go run -race server.go 选项测试您的服务器,然后消除它抛出的所有竞争条件。这样,您可以在错误发生之前更轻松地消除这些错误。

Tree

请参阅golang race detector