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

Dictionary Golang致命错误:并发映射读取和映射写入,dictionary,go,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" "buf

我在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())
}
目前,服务器只是一个“边缘”。

一般来说(没有访问出错代码的权限),您有几个选项。以下是其中两个:

sync.RWMutex 使用
sync.RWMutex{}
控制对映射的访问。如果在映射上有单个读取和写入,而不是循环,请使用此选项。看

这里是一个通过
someMapMutex
访问
someMap
的示例:

var (
    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
。此地图已经解决了种族问题,但根据您的使用情况,速度可能会较慢的主要优点在于for循环。看

一般意见 您应该使用
-race
选项测试服务器,然后消除它引发的所有竞争条件。这样,您可以更容易地在错误发生之前消除这些错误

go run -race server.go

请参见

我使用了完整的代码好的,很抱歉我的错误,可能不是映射错误/root/work/src/github.com/user/imoobler/utils.go:33我已经在上面发布了我的源代码。你能运行
-race
检测吗?当然,使用-race参数后出现完全错误在我看来,行号似乎已更改为启用的代码
limbo.go:44
似乎是正确的,并指向此代码
for u,player:=range players{
。我假设
players
映射是由多个go例程访问的。您需要控制对此
映射的访问(通过
互斥体
/
RWMutex
)或按照我的回答中的建议使用
syncmap
go run -race server.go