首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我的golang锁释放队列总是卡在那里?

为什么我的golang锁释放队列总是卡在那里?
EN

Stack Overflow用户
提问于 2012-09-06 09:52:20
回答 2查看 3K关注 0票数 2

下面是我的代码:

代码语言:javascript
复制
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()
}

问题是,有时代码运行得很好,但有时它会失败,只是卡住了没有响应。

我的代码中有什么问题吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-09-07 02:23:11

下面是用@mkb建议的通道重写的上述代码(不包括无限队列大小)。

它锁不住了。

我建议你使用通道,除非你有很好的理由不这样做,因为Go团队已经花费了大量的精力来使它们可靠、高性能和易于使用。

代码语言:javascript
复制
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()
}
票数 3
EN

Stack Overflow用户

发布于 2013-01-21 21:52:25

在这段代码中有很多活跃的等待,我强烈建议像Nick的好代码一样干净地使用channel。

然而,这是我对原始问题“为什么被卡住了?”的确切答案:没有保证每个goroutine何时会让步,让其他goroutine执行,最有可能的是,当它在无限循环中时,它永远不会让步。

你可以通过在每个可能无限的for循环中使用runtime.Gosched()来解决这个问题:

产生处理器,允许其他goroutine运行。它不会挂起当前的goroutine,因此执行会自动恢复。

这个增强的代码运行速度几乎和原始代码一样快,但永远不会挂起:

代码语言:javascript
复制
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()
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12291949

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档