这是我的代码:
package main
import (
"sync/atomic"
"unsafe"
"sync"
"fmt"
"time"
)
const (
MAX_DATA_SIZE = 100
)
// lock free queue
type Queue struct {
head unsafe.Pointer
tail unsafe.Pointer
}
// one node in queue
type Node struct {
val interface{}
next unsafe.Pointer
}
// queue functions
func (self *Queue) enQueue(val interface{}) {
newValue := unsafe.Pointer(&Node{val: val, next: nil})
var tail,next unsafe.Pointer
for {
tail = self.tail
next = ((*Node)(tail)).next
if next != nil {
atomic.CompareAndSwapPointer(&(self.tail), tail, next)
}else if atomic.CompareAndSwapPointer(&((*Node)(tail).next), nil, newValue){
break
}
}
}
func (self *Queue) deQueue() (val interface{}, success bool){
var head,tail,next unsafe.Pointer
for {
head = self.head
tail = self.tail
next = ((*Node)(head)).next
if head == tail {
if next == nil {
return nil, false
}else {
atomic.CompareAndSwapPointer(&(self.tail), tail, next)
}
}else {
val = ((*Node)(next)).val
if atomic.CompareAndSwapPointer(&(self.head), head, next) {
return val, true
}
}
}
return
}
func main() {
var wg sync.WaitGroup
wg.Add(20)
queue := new(Queue)
queue.head = unsafe.Pointer(new(Node))
queue.tail = queue.head
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < MAX_DATA_SIZE; j++ {
t := time.Now()
queue.enQueue(t)
fmt.Println("enq = ", t)
}
}()
}
for i := 0; i < 10; i++ {
go func() {
ok := false
var val interface{}
defer wg.Done()
for j := 0; j < MAX_DATA_SIZE; j++ {
val,ok = queue.deQueue()
for !ok {
val,ok = queue.deQueue()
}
fmt.Println("deq = ",val)
}
}()
}
wg.Wait()
}
问题是,有时代码运行正常,但有时它会失败并且只是没有响应而停滞不前。
我的代码有问题吗?
答案 0 :(得分:4)
这段代码中有很多活跃的等待,我强烈建议像Nick的漂亮代码一样干净利用频道。
然而,这是我对确切原始问题“为何被卡住了?”的回答。 :没有保证每个goroutine会让其他人执行,并且很可能在无限循环内它永远不会屈服。
你可以通过在每个可能无限的for循环中使用runtime.Gosched()来解决这个问题:
Gosched产生处理器,允许其他goroutines运行。它 不暂停当前goroutine,因此执行恢复 自动。
此增强型代码的运行速度几乎与原始代码一样快,但从不挂起:
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
"time"
"unsafe"
)
const (
MAX_DATA_SIZE = 100
)
// lock free queue
type Queue struct {
head unsafe.Pointer
tail unsafe.Pointer
}
// one node in queue
type Node struct {
val interface{}
next unsafe.Pointer
}
// queue functions
func (self *Queue) enQueue(val interface{}) {
newValue := unsafe.Pointer(&Node{val: val, next: nil})
var tail, next unsafe.Pointer
for {
tail = self.tail
next = ((*Node)(tail)).next
if next != nil {
atomic.CompareAndSwapPointer(&(self.tail), tail, next)
} else if atomic.CompareAndSwapPointer(&((*Node)(tail).next), nil, newValue) {
break
}
runtime.Gosched()
}
}
func (self *Queue) deQueue() (val interface{}, success bool) {
var head, tail, next unsafe.Pointer
for {
head = self.head
tail = self.tail
next = ((*Node)(head)).next
if head == tail {
if next == nil {
return nil, false
} else {
atomic.CompareAndSwapPointer(&(self.tail), tail, next)
}
} else {
val = ((*Node)(next)).val
if atomic.CompareAndSwapPointer(&(self.head), head, next) {
return val, true
}
}
runtime.Gosched()
}
return
}
func main() {
var wg sync.WaitGroup
wg.Add(20)
queue := new(Queue)
queue.head = unsafe.Pointer(new(Node))
queue.tail = queue.head
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < MAX_DATA_SIZE; j++ {
t := time.Now()
queue.enQueue(t)
fmt.Println("enq = ", t)
}
}()
}
for i := 0; i < 10; i++ {
go func() {
ok := false
var val interface{}
defer wg.Done()
for j := 0; j < MAX_DATA_SIZE; j++ {
val, ok = queue.deQueue()
for !ok {
val, ok = queue.deQueue()
runtime.Gosched()
}
fmt.Println("deq = ", val)
}
}()
}
wg.Wait()
}
答案 1 :(得分:3)
以上是使用@mkb建议的频道重写(禁止无限队列大小)。
它没有锁定。
我建议您使用频道,除非您有充分的理由不去,因为Go团队花费了大量精力使其可靠,高性能且易于使用。
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
const (
MAX_DATA_SIZE = 100
)
func main() {
runtime.GOMAXPROCS(4)
var wg sync.WaitGroup
wg.Add(20)
queue := make(chan time.Time, 10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < MAX_DATA_SIZE; j++ {
t := time.Now()
queue <- t
fmt.Println("enq = ", t)
}
}()
}
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < MAX_DATA_SIZE; j++ {
val := <-queue
fmt.Println("deq = ", val)
}
}()
}
wg.Wait()
}