Go 正在等待具有超时的sync.Cond

Go 正在等待具有超时的sync.Cond,go,sync,Go,Sync,是否有可能以某种简单的方式实现Java的等效功能 wait(long timeMillis) 哪一个在监视器上等待(大致为互斥+秒)指定的时间量,如果没有发出信号则返回 我在文档中找不到任何东西,也无法在谷歌上搜索,虽然当然可以玩一些游戏,创建一个WaitGroup,并设置一个计时器goroutine pop,但仅仅获得这个简单的功能似乎很乏味/烦人/低效(顺便说一句,我遇到的任何底层系统线程库都直接支持它) 编辑:是的,我们也都读过了-再说一次,创建更多线程是一个“糟糕”(无性能)的解决方案

是否有可能以某种简单的方式实现Java的等效功能

wait(long timeMillis)
哪一个在监视器上等待(大致为互斥+秒)指定的时间量,如果没有发出信号则返回

我在文档中找不到任何东西,也无法在谷歌上搜索,虽然当然可以玩一些游戏,创建一个WaitGroup,并设置一个计时器goroutine pop,但仅仅获得这个简单的功能似乎很乏味/烦人/低效(顺便说一句,我遇到的任何底层系统线程库都直接支持它)


编辑:是的,我们也都读过了-再说一次,创建更多线程是一个“糟糕”(无性能)的解决方案,通道也不太适合这个解决方案。想象一个典型的并发服务器Join()方法的用例。。。(请不要告诉我反转控件并使用侦听器模式。您并不总是有机会更改正在使用的API…

不。没有简单的方法可以做到这一点,并且基于该线程,他们不会添加侦听器模式。(尽管与他们讨论可能会让您有所收获)

但总有一条艰难之路。2个选项:

  • 滚动您自己的具有此功能的
    Cond
    。(请参阅)
  • 通过系统调用使用操作系统级功能。(可能是?)
  • 这里的挑战——以及它之所以不平凡的原因——是goroutines不是线程。Go有自己的自定义调度程序。创建自己的
    Cond
    需要修补运行时中不需要修补的部分。(但是,正如我所说,这是可能的)

    如果这是限制的话,很抱歉。go的大部分都非常简单-你可以经常跳到较低的一层,而不会有太多麻烦。但是调度程序不是这样的。它很神奇


    这种魔力适用于大多数情况,他们在
    sync
    中添加了这些东西,以涵盖一些已知的情况,但事实并非如此。如果你觉得你找到了另一种,也许你可以说服他们添加它。(但这不仅仅是从另一种编程语言复制API,或者公开底层API的问题)

    我遇到了同样的问题,结果证明使用频道很容易解决

    • 信号是通道上的发送信号
    • 等待只是在通道上等待消息
    • 带超时的等待只是对计时器和消息的选择
    • 广播是一个循环发送消息,直到没有人听为止
    与任何条件变量一样,在等待时需要保持互斥,强烈建议在发信号时保持互斥

    我编写了一个遵循Cond协议的in实现,并添加了一个WaitOrTimeout。如果成功,它将返回true,如果超时,它将返回false

    以下是我的代码和一些测试用例!免责声明:这似乎工作正常,但尚未经过彻底测试。此外,公平性也无法保证。等待的线程按照调度程序认为合适的顺序释放,不一定是先到先得。

    主程序包
    进口(
    “同步”
    “时间”
    “fmt”
    )
    类型TMOCond结构{
    我同步,储物柜
    陈波
    }
    func NewTMOCond(l同步锁)*TMOCond{
    返回和TMOCond{ch:make(chanbool),L:L}
    }
    func(t*TMOCond)Wait(){
    t、 L.解锁()
    
    您可以使用一个频道实现一个仅支持广播(无信号)的条件变量。以下是它的简要要点:

    您也可以使用此技术在代码中替换频道并关闭旧频道。Gist中的代码使用不安全的指针和原子操作来允许在不获取主sync.Locker的情况下调用“Broadcast”。但是在您自己的代码中,您通常应该从获取锁中进行广播所以你不需要做任何不安全/原子的事情


    虽然这种方法可行,但您可能还想签出:。如果您制作的加权信号量的限制为1,这也将为您提供所需的所有功能,这也是公平的。

    我在今年的GopherCon演讲中草拟了两个可能的备选方案(请参阅)。条件变量本节从幻灯片37开始,但备份幻灯片(101-105)将更详细地介绍此特定模式

    正如zviadm所指出的,一个选项()是关闭一个通道

    另一个选项()是让每个服务员分配一个1缓冲通道,并让广播员向缓冲区发送一个令牌进行广播

    如果事件是持续状态,例如“队列为空”,则第三个选项()是使用1缓冲通道,并让每个接收器在持续状态下重新填充缓冲区。

    使等待超时成为可能。请参阅示例:

    package main
    
    import (
        "fmt"
        "sync"
        "time"
        "gitlab.com/jonas.jasas/condchan"
    )
    
    func main() {
        cc := condchan.New(&sync.Mutex{})
        timeoutChan := time.After(time.Second)
    
        cc.L.Lock()
        // Passing func that gets channel c that signals when
        // Signal or Broadcast is called on CondChan
        cc.Select(func(c <-chan struct{}) { // Waiting with select
            select {
            case <-c: // Never ending wait
            case <-timeoutChan:
                fmt.Println("Hooray! Just escaped from eternal wait.")
            }
        })
        cc.L.Unlock()
    }
    
    主程序包
    进口(
    “fmt”
    “同步”
    “时间”
    “gitlab.com/jonas.jasas/condchan”
    )
    func main(){
    cc:=condchan.New(&sync.Mutex{})
    timeoutChan:=时间后(时间秒)
    cc.L.Lock()
    //传递func,获取通道c,当
    //在CondChan上调用信号或广播
    
    cc.Select(func)(c)值得一读:你在使用appengine吗?特别是在Tim Cooper给出的链接上,请注意Ian Lance Taylor关于“条件变量通常不适合在Go中使用”的评论。不要试图用编写Java的方式编写Go。@TimCooper-很有趣,谢谢你的链接。但我看到的是“这是处理您的用例的错误方法”,并且…通道和“侧定时器”(goroutine/w timer)都不适用于该线程中的OP(或我)。他们只是关闭了他并关闭了他的票据。=/“这如何应用于我的示例?”“:你没有给出一个例子!你要求的是一个没有任何上下文的特定特性/函数,所以我认为这是可能的(基于这里糟糕的问题水平,其中包含“如何执行Caleb-与我在阻止tcp Accept()调用时使用的相同技巧
    package main
    
    import (
        "fmt"
        "sync"
        "time"
        "gitlab.com/jonas.jasas/condchan"
    )
    
    func main() {
        cc := condchan.New(&sync.Mutex{})
        timeoutChan := time.After(time.Second)
    
        cc.L.Lock()
        // Passing func that gets channel c that signals when
        // Signal or Broadcast is called on CondChan
        cc.Select(func(c <-chan struct{}) { // Waiting with select
            select {
            case <-c: // Never ending wait
            case <-timeoutChan:
                fmt.Println("Hooray! Just escaped from eternal wait.")
            }
        })
        cc.L.Unlock()
    }