登录
首页 >  Golang >  Go问答

如何正确使用sync.Cond?

来源:Golang技术栈

时间:2023-04-12 19:53:49 101浏览 收藏

哈喽!今天心血来潮给大家带来了《如何正确使用sync.Cond?》,想必大家应该对Golang都不陌生吧,那么阅读本文就都不会很困难,以下内容主要涉及到golang,若是你正在学习Golang,千万别错过这篇文章~希望能帮助到你!

问题内容

我无法弄清楚如何正确使用sync.Cond. 据我所知,锁定 Locker 和调用条件的 Wait 方法之间存在竞争条件。这个例子在主 goroutine 的两行之间添加了一个人为的延迟来模拟竞态条件:

package main

import (
    "sync"
    "time"
)

func main() {
    m := sync.Mutex{}
    c := sync.NewCond(&m)
    go func() {
        time.Sleep(1 * time.Second)
        c.Broadcast()
    }()
    m.Lock()
    time.Sleep(2 * time.Second)
    c.Wait()
}

[在运动场上奔跑]

这会立即引起恐慌:

致命错误:所有 goroutine 都处于休眠状态 - 死锁!

goroutine 1 [semacquire]:
sync.runtime_Syncsemacquire(0x10330208, 0x1)
    /usr/local/go/src/runtime/sema.go:241 +0x2e0
sync.(*Cond).Wait(0x10330200, 0x0)
    /usr/local/go/src/sync/cond.go:63 +0xe0
main.main()
    /tmp/sandbox301865429/main.go:17 +0x1a0

我究竟做错了什么?如何避免这种明显的竞争条件?我应该使用更好的同步结构吗?


编辑: 我意识到我应该更好地解释我在这里试图解决的问题。我有一个长时间运行的 goroutine,它下载一个大文件和许多其他 goroutine,它们需要在可用时访问 HTTP 标头。这个问题比听起来更难。

我不能使用通道,因为只有一个 goroutine 会收到该值。并且其他一些 goroutine 会在它们已经可用很久之后尝试检索它们。

下载器 goroutine 可以简单地将 HTTP 标头存储在一个变量中,并使用互斥锁来保护对它们的访问。然而,这并没有为其他 goroutines 提供一种“等待”它们变得可用的方法。

我曾认为 async.Mutexsync.Condtogether 都可以实现这个目标,但似乎这是不可能的。

正确答案

我终于找到了一种方法来做到这一点,它根本不涉及sync.Cond- 只是互斥体。

type Task struct {
    m       sync.Mutex
    headers http.Header
}

func NewTask() *Task {
    t := &Task{}
    t.m.Lock()
    go func() {
        defer t.m.Unlock()
        // ...do stuff...
    }()
    return t
}

func (t *Task) WaitFor() http.Header {
    t.m.Lock()
    defer t.m.Unlock()
    return t.headers
}

这是如何运作的?

互斥锁在任务开始时被锁定,确保任何调用WaitFor()都会阻塞。一旦标头可用并且 goroutine 解锁了互斥锁,每次调用WaitFor()都会一次执行一个。所有未来的调用(即使在 goroutine 结束之后)都不会出现锁定互斥锁的问题,因为它总是处于解锁状态。

好了,本文到此结束,带大家了解了《如何正确使用sync.Cond?》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!

声明:本文转载于:Golang技术栈 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>