Golang Go语言用切片实现队列遇到无法理解的问题

发布于 1周前 作者 phonegap100 来自 Go语言

Golang Go语言用切片实现队列遇到无法理解的问题

package main

import ( “fmt” “log” “net/http” “os” “time” )

var Q []string

func main() { go func() { time.Sleep(1 * time.Second) for { test() } }() Q = append(Q, “233”) Q = append(Q, “233”) Q = append(Q, “233”)

time.Sleep(3 * time.Second)
Q = append(Q, "233")
Q = append(Q, "233")
Q = append(Q, "233")
Q = append(Q, "233")
Q = append(Q, "233")

time.Sleep(3 * time.Second)
Q = append(Q, "233")
time.Sleep(3 * time.Second)
Q = append(Q, "233")

http.HandleFunc("/", handle)
err := http.ListenAndServe(":8080", nil)
if err != nil {
	log.Println("http port has been used.")
	os.Exit(-1)
}

} func handle(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() _ = r.ParseForm() _, _ = fmt.Fprint(w, “hello”)

} func test() { file := queueOut() if file != “” { log.Println(file) }

}

func queueOut() string { res := Q[0] if len(Q) == 1 { Q[0] = “” return res } Q = Q[1:] return res }

我觉得我这样写没错啊. 但是运行的时候有几率出现

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1058fe5]

goroutine 19 [running]: fmt.(*buffer).WriteString(…) /opt/local/lib/go/src/fmt/print.go:82 fmt.(*fmt).padString(0xc000114040, 0x0, 0x3) /opt/local/lib/go/src/fmt/format.go:110 +0x9d fmt.(*fmt).fmtS(0xc000114040, 0x0, 0x3) /opt/local/lib/go/src/fmt/format.go:328 +0x61 fmt.(*pp).fmtString(0xc000114000, 0x0, 0x3, 0x76) /opt/local/lib/go/src/fmt/print.go:437 +0x122 fmt.(*pp).printArg(0xc000114000, 0x1247a20, 0xc000010050, 0x76) /opt/local/lib/go/src/fmt/print.go:671 +0x878 fmt.(*pp).doPrintln(0xc000114000, 0xc0000cffa8, 0x1, 0x1) /opt/local/lib/go/src/fmt/print.go:1146 +0xb1 fmt.Sprintln(0xc0000cffa8, 0x1, 0x1, 0x1247a20, 0x1) /opt/local/lib/go/src/fmt/print.go:271 +0x52 log.Println(0xc0000cffa8, 0x1, 0x1) /opt/local/lib/go/src/log/log.go:301 +0x3f main.test() /Users/licsber/go/src/test/main.go:52 +0xd7 main.main.func1() /Users/licsber/go/src/test/main.go:17 +0x2f created by main.main /Users/licsber/go/src/test/main.go:14 +0x39

Process finished with exit code 2

小白表示无法理解 这个问题是几率出现. 每次运行不一定都有


更多关于Golang Go语言用切片实现队列遇到无法理解的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

18 回复

要加锁吧

更多关于Golang Go语言用切片实现队列遇到无法理解的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


slice 的线程安全问题,加锁?


感谢 我去试试

package main

import (
“fmt”
“log”
“net/http”
“os”
“sync”
“time”
)

var Q []string

var lock sync.RWMutex

func main() {
go func() {
time.Sleep(1 * time.Second)
for {
test()
}
}()
Q = append(Q, “233”)
Q = append(Q, “233”)
Q = append(Q, “233”)

time.Sleep(3 * time.Second)
Q = append(Q, “233”)
Q = append(Q, “233”)
Q = append(Q, “233”)
Q = append(Q, “233”)
Q = append(Q, “233”)

time.Sleep(3 * time.Second)
Q = append(Q, “233”)
time.Sleep(3 * time.Second)
Q = append(Q, “233”)

http.HandleFunc("/", handle)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Println(“http port has been used.”)
os.Exit(-1)
}
}
func handle(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
_ = r.ParseForm()
_, _ = fmt.Fprint(w, “hello”)

}
func test() {
file := queueOut()
if file != “” {
log.Println(file)
}

}

func queueOut() string {
// 加写锁
lock.Lock()
res := Q[0]
if len(Q) == 1 {
Q[0] = “”
return res
}
Q = Q[1:]
lock.Unlock()
return res
}

有 channel 不用非要用非线程安全的 slice

另外 slice 的 append 操作是会重新分配 slice 结构的,而且你的 slice 初始 len 为 0,每次扩容时存储数据的 array 也会重新分配,问题应该就出现在这里

好像这样子之后, 会有数据没有被加到队列里去?

channel 是啥… 刚学 go 还没看到 我去看看 谢谢

大哥你这个 … channel 啊

Q = Q[1:]瞬间执行多次,这个并发直接把 Q 给 cut 没了,再访问 Q 的任意下标自然就崩了。

要非得用 slice 就别直接操作 slice,另外封装两个增删方法,加上锁。


func queueOut() string {
res := Q[0]
if len(Q) == 1 {
Q[0] = “”
return res
}
// 加写锁
lock.Lock()
Q = Q[1:]
lock.Unlock()
return res
}

channel 没学等于没学 go

感谢 成功解决问题

对, 看了 channel 之后恍然大悟 谢谢

刚看到 go routine , 感谢
现在接触的 go 给我一种我一直在处理错误(err)的错觉

#12 channel 是 go 的神器啊,怎么能不用呢

刚刚看到 go 的 channel , 感谢!

在Go语言中,使用切片来实现队列是一个常见的操作,但确实可能会遇到一些初学者难以理解的问题。以下是一些可能的原因和解决方法:

  1. 切片扩容:Go语言的切片在容量不足时会自动扩容,但扩容机制可能会导致队列操作的复杂度增加,特别是当队列频繁进行入队和出队操作时。如果队列长度变化较大,建议考虑使用环形缓冲区或者链式队列来优化性能。

  2. 边界条件处理:在处理队列的边界条件时,需要特别注意切片的起始和结束位置。例如,在出队时,如果队列为空(即切片长度为0),继续出队可能会导致越界错误。

  3. 并发访问:如果队列在多个goroutine之间共享,需要确保使用适当的同步机制(如互斥锁)来避免数据竞争。

  4. 切片操作误解:有时,对切片操作的误解(如误用append函数导致的切片重新分配)也会导致队列实现出现问题。确保理解切片的基本操作,如append、copy等。

  5. 内存管理:长时间运行的队列可能会占用大量内存,特别是当队列元素是复杂数据结构时。考虑定期清理不再需要的元素,以释放内存。

如果具体遇到的是代码实现上的问题,建议提供详细的代码片段和错误信息,以便更准确地定位问题所在。同时,参考Go语言的官方文档和社区资源,如Stack Overflow,也是解决问题的有效途径。

回到顶部