0

作为我的第一个 golang 项目,我正在开发一个 slack bot,虽然这个特定的 bot 命令的功能很好,但它可能会随机恐慌并引发错误。

我能够确定我正在进行数据竞赛,特别是我的两个 go 例程。但是我很难确定如何修补它们,或者做互斥锁是否是处理共享变量的正确方法。

我试图弄清楚问题是我的结果变量,它被分成通道,每个通道都由 go 例程处理,或者它是我的缓冲区变量,os.exec 包使用它来运行两个标准输出的命令和 stderr 修改。

这是下面的代码和数据竞争示例。

package reboot

import (

    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "os/exec"
    "regexp"
)

// to handle json keys
type rebootObject struct {
    Name    string
    Command string
}

var (
    fileNotFound = "config file not found"
    cmdNotFound  = "Error finding command argument"
)

// *bot.Cmd is the first arg to be passed to the bot
// bot.CmdResultV3 wants message from go routine and done = true
func Reboot(command *bot.Cmd) (result bot.CmdResultV3, err error) {
    result = bot.CmdResultV3{Message: make(chan string), Done: make(chan bool, 1)}

    // load json config file with names/commands
    filePath := "./plugins/reboot/config.json"
    file, err1 := ioutil.ReadFile(filePath)
    if err1 != nil {
        bot.Reply(command.Channel, fileNotFound, command.User)
    }

    var scriptParse []rebootObject
    //userinput := "box4535345346" // faking user input
    userinput := command.Args[0] // real one
    err2 := json.Unmarshal(file, &scriptParse)
    if err2 != nil {
        fmt.Println("error:", err2)
        bot.Reply(command.Channel, fileNotFound, command.User)
    }
    //strip numbers off input to match json key
    reg, err := regexp.Compile("[^a-zA-Z]+")
    if err != nil {
        log.Fatal(err)
    }
    // loop through json file to find the match of user input to json name key
    cmdFound := false
    for k := range scriptParse {
        newinput := reg.ReplaceAllString(userinput, "")
        // keep running for loop until names do match
        if scriptParse[k].Name != newinput {
            continue
        }
        cmdFound = true

        cmd := exec.Command("/bin/bash", "-c", scriptParse[k].Command)
        var b bytes.Buffer
        cmd.Stdout = &b
        cmd.Stderr = &b
        err = cmd.Start()
        if err != nil {
            return
        }
        done := false
        go func() {
            cmd.Wait()
            done = true
            result.Done <- true
        }()
        go func() {
            for {
                line, _ := b.ReadString('\n')
                if line != "" {
                    result.Message <- line
                }
                if done {
                    close(result.Message)
                    break
                }

            }
        }()
    }
    if cmdFound == false {
        result.Done <- true
        bot.Reply(command.Channel, cmdNotFound, command.User)
    }
    return result, nil
}

数据竞赛:

==================
WARNING: DATA RACE
Read at 0x00c420582558 by goroutine 37:
  bytes.(*Buffer).readSlice()
      /usr/local/go/src/bytes/buffer.go:421 +0x48
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:440 +0x45
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c420582558 by goroutine 35:
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:92 +0x608
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Read at 0x00c420582540 by goroutine 37:
  bytes.(*Buffer).readSlice()
      /usr/local/go/src/bytes/buffer.go:421 +0x62
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:440 +0x45
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c420582540 by goroutine 35:
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:91 +0x5d0
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Write at 0x00c420582560 by goroutine 37:
  bytes.(*Buffer).readSlice()
      /usr/local/go/src/bytes/buffer.go:429 +0x186
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:440 +0x45
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c420582560 by goroutine 35:
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:191 +0x5f
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Read at 0x00c42014cc00 by goroutine 37:
  runtime.slicebytetostring()
      /usr/local/go/src/runtime/string.go:72 +0x0
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:441 +0x84
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c42014cc00 by goroutine 35:
  internal/race.WriteRange()
      /usr/local/go/src/internal/race/race.go:49 +0x42
  syscall.Read()
      /usr/local/go/src/syscall/syscall_unix.go:165 +0x9a
  internal/poll.(*FD).Read()
      /usr/local/go/src/internal/poll/fd_unix.go:122 +0x1a0
  os.(*File).read()
      /usr/local/go/src/os/file_unix.go:216 +0x70
  os.(*File).Read()
      /usr/local/go/src/os/file.go:103 +0x8e
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:209 +0x1dd
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Read at 0x00c4202d2600 by goroutine 37:
  runtime.slicebytetostring()
      /usr/local/go/src/runtime/string.go:72 +0x0
  bytes.(*Buffer).ReadString()
      /usr/local/go/src/bytes/buffer.go:441 +0x84
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:79 +0x41

Previous write at 0x00c4202d2600 by goroutine 35:
  runtime.slicecopy()
      /usr/local/go/src/runtime/slice.go:160 +0x0
  bytes.(*Buffer).ReadFrom()
      /usr/local/go/src/bytes/buffer.go:205 +0x4b2
  io.copyBuffer()
      /usr/local/go/src/io/io.go:386 +0x348
  io.Copy()
      /usr/local/go/src/io/io.go:362 +0x7e
  os/exec.(*Cmd).writerDescriptor.func1()
      /usr/local/go/src/os/exec/exec.go:264 +0x68
  os/exec.(*Cmd).Start.func1()
      /usr/local/go/src/os/exec/exec.go:380 +0x34

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 35 (running) created at:
  os/exec.(*Cmd).Start()
      /usr/local/go/src/os/exec/exec.go:379 +0xa6b
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:67 +0x7c8
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
==================
WARNING: DATA RACE
Write at 0x00c4202008c8 by goroutine 36:
  github.com/owner/gobot/plugins/reboot.Reboot.func1()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:74 +0x4d

Previous read at 0x00c4202008c8 by goroutine 37:
  github.com/owner/gobot/plugins/reboot.Reboot.func2()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:83 +0x61

Goroutine 36 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:72 +0x846
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0

Goroutine 37 (running) created at:
  github.com/owner/gobot/plugins/reboot.Reboot()
      /Users/macowner/go/src/github.com/owner/gobot/plugins/reboot/reboot.go:77 +0x885
  github.com/owner/gobot/bot.(*Bot).handleCmd()
      /Users/macowner/go/src/github.com/owner/gobot/bot/cmd.go:240 +0x13b
  github.com/owner/gobot/bot.(*Bot).MessageReceived()
      /Users/macowner/go/src/github.com/owner/gobot/bot/bot.go:101 +0x5a8
  github.com/owner/gobot/bot.Run()
      /Users/macowner/go/src/github.com/owner/gobot/bot/reply.go:142 +0xdb3
  main.main()
      /Users/macowner/go/src/github.com/owner/gobot/main.go:46 +0x9b0
==================
4

2 回答 2

0

rabhis 是正确的,您可以使用互斥锁嵌入缓冲区,例如:

https://gist.github.com/arkan/5924e155dbb4254b64614069ba0afd81

package safebuffer

import (
    "bytes"
    "sync"
)

// Buffer is a goroutine safe bytes.Buffer
type Buffer struct {
    buffer bytes.Buffer
    mutex  sync.Mutex
}

// Write appends the contents of p to the buffer, growing the buffer as needed. It returns
// the number of bytes written.
func (s *Buffer) Write(p []byte) (n int, err error) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.buffer.Write(p)
}

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (s *Buffer) String() string {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    return s.buffer.String()
}
于 2018-02-14T04:42:07.040 回答
0

第一个以 开头的 goroutinecmd.Wait()正在写入缓冲区b,而另一个以 开头的 goroutineline, _ := b.ReadString('\n')正在读取缓冲区。这就是发生数据竞赛的原因。修改代码,使执行结果只有在执行完成后才能读取。

于 2018-02-13T17:23:51.493 回答