等待sync.Cond超时

时间:2015-04-28 15:32:41

标签: go sync

是否可以通过一些简单的方式来完成相同的Java

wait(long timeMillis)

在指定的时间内在监视器上(大致为mutex + cond)等待,如果没有发出信号则返回?

我无法在文档中找到任何内容或在谷歌上搜索,虽然它当然可以通过制作一个WaitGroup和定时器goroutine pop来玩一些游戏,但这似乎很乏味/烦人/只能获得这个简单的功能(这是我曾经遇到的任何底层系统线程库直接支持的方式)

编辑:是的,我们都阅读了http://www.golang-book.com/10/index.htm以及https://blog.golang.org/pipelines - 再一次,创建更多线程是一个"坏" (非性能)解决方案,渠道也不太适合这个。想象一个用例是一个典型的并发服务器Join()方法......(请不​​要告诉我反转控件并使用一个Listener模式。你不总是有权改变你工作的API与...)

5 个答案:

答案 0 :(得分:5)

您可以使用通道实现支持仅广播(无信号)的条件变量。这是一个快速的要点: https://gist.github.com/zviadm/c234426882bfc8acba88f3503edaaa36#file-cond2-go

您也可以使用这种替换频道并在代码中关闭旧频道的技巧。 Gist中的代码使用unsafe.Pointer和atomic操作来允许调用' Broadcast'没有获得主同步.Locker。但是,在您自己的代码中,您通常应该从获取锁中进行广播,这样您就不需要做任何不安全/原子的事情。

虽然此方法有效,但您可能还需要结帐:https://godoc.org/golang.org/x/sync/semaphore。如果你制作一个限制为1的加权信号量,这也将为你提供所需的所有能力,这也是公平的。

答案 1 :(得分:0)

没有。没有简单的方法可以做到这一点,并且基于该线程他们不会添加一个。 (虽然或许与他们讨论它可能会让你到处)

但总有一种困难的方式。 2个选项:

  1. 滚动自己拥有此功能的Cond。 (见https://golang.org/src/sync/cond.go
  2. 通过系统调用使用操作系统级功能。 (也许是futex?)
  3. 这里的挑战 - 以及它不是微不足道的原因 - 是goroutines不是线程。 Go拥有自己的自定义调度程序。创建自己的Cond将涉及修补运行时的部分内容,这些部分并不是真正意义上的修补。 (但是,就像我说的,这是可能的)

    很抱歉,如果这是限制性的。大部分时间都非常简单 - 您可以经常跳到较低层而不会有太多麻烦。但是调度程序不是那样的。这是魔法。

    魔法适用于大多数事物,他们在sync中添加了一些内容,以涵盖一些已知的情况。如果您觉得自己找到了另一个,也许您可​​以说服他们添加它。 (但这不仅仅是从另一种编程语言中复制API,还是暴露底层API)

答案 2 :(得分:0)

我在今年的GopherCon演讲中草拟了几种可能的选择(请参阅https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view)。 “条件变量”部分从幻灯片37开始,但是备用幻灯片(101-105)中更详细地介绍了此特定模式。

zviadm指出,一种选择(https://play.golang.org/p/tWVvXOs87HX)是关闭频道。

另一种选择(https://play.golang.org/p/uRwV_i0v13T)是让每个服务员分配一个1缓冲的频道,并让广播公司将令牌发送到缓冲区中进行广播。

如果事件是持久性条件,例如“队列为空”,则第三个选项(https://play.golang.org/p/uvx8vFSQ2f0)使用1缓冲通道,并让每个接收方在条件满足的情况下重新填充缓冲区持续存在。

答案 3 :(得分:0)

https://gitlab.com/jonas.jasas/condchan使等待时超时成为可能。请查看示例:

# pylint: disable=unused-variable, wildcard-import, method-hidden
# pylint: enable=too-many-lines

答案 4 :(得分:-2)

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

  • 信号是频道上的发送
  • 等待只是在等待频道上的消息。
  • 等待超时只是对计时器和消息的选择。
  • 广播是一个发送消息的循环,直到没有人听到。

与任何条件变量一样,在等待时需要保持互斥锁,强烈建议在发出信号时保持它。

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

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

https://play.golang.org/p/K1veAOGbWZ

package main

import (
    "sync"
    "time"
    "fmt"
)

type TMOCond struct {
    L    sync.Locker
    ch      chan bool
}

func NewTMOCond(l sync.Locker) *TMOCond {
    return &TMOCond{ ch: make(chan bool), L: l }
}

func (t *TMOCond) Wait() {
    t.L.Unlock()
    <-t.ch
    t.L.Lock()
}

func (t *TMOCond) WaitOrTimeout(d time.Duration) bool {
    tmo := time.NewTimer(d)
    t.L.Unlock()
    var r bool
    select {
    case <-tmo.C:
    r = false
    case <-t.ch:
        r = true
    }
    if !tmo.Stop() {
        select {
        case <- tmo.C:
        default:
        }
    }
    t.L.Lock()
    return r
}

func (t *TMOCond) Signal() {
    t.signal()
}

func (t *TMOCond) Broadcast() {
    for {
        // Stop when we run out of waiters
        //
        if !t.signal() {
            return
        }
    }
}

func (t *TMOCond) signal() bool {
    select {
    case t.ch <- true:
        return true
    default:
        return false
    }
}

// **** TEST CASES ****
func lockAndSignal(t *TMOCond) {
    t.L.Lock()
    t.Signal()
    t.L.Unlock()
}

func waitAndPrint(t *TMOCond, i int) {
    t.L.Lock()
    fmt.Println("Goroutine", i, "waiting...")
    ok := t.WaitOrTimeout(10 * time.Second)
    t.L.Unlock()
    fmt.Println("This is goroutine", i, "ok:", ok)
}

func main() {
    var m sync.Mutex
    t := NewTMOCond(&m)

    // Simple wait
    //
    t.L.Lock()
    go lockAndSignal(t)
    t.Wait()
    t.L.Unlock()
    fmt.Println("Simple wait finished.")

    // Wait that times out
    //
    t.L.Lock()
    ok := t.WaitOrTimeout(100 * time.Millisecond)
    t.L.Unlock()
    fmt.Println("Timeout wait finished. Timeout:", !ok)


    // Broadcast. All threads should finish.
    //
    for i := 0; i < 10; i++ {
        go waitAndPrint(t, i)
    }
    time.Sleep(1 * time.Second) 
    t.L.Lock()
    fmt.Println("About to signal")
    t.Broadcast()
    t.L.Unlock()
    time.Sleep(10 * time.Second)
}