使用通道或sync.Cond来等待条件。

huangapple go评论91阅读模式
英文:

Using channel or sync.Cond to wait for condition

问题

我正在尝试等待特定条件的发生,并且我想知道如何最好地实现。我有一个简化版的结构体如下:

type view struct {
    timeFrameReached bool
    Rows []*sitRow
}

在一个 goroutine 中,我正在更新一个文件,并将其读入 view 变量中。行数增加,timeFrameReached 最终会变为 true

在其他地方,我想要等待以下条件成立:

view.timeFrameReached == true || len(view.Rows) >= numRows

我正在尝试学习通道和 Go 的条件变量是如何工作的,我想知道这里最好的解决方案是什么。从理论上讲,我可以做一些简单的事情,比如:

for {
    view = getView()
    if view.timeFrameReached == true || len(view.Rows) >= numRows {
        break
    }
}

但这显然是一个简单的解决方案。numRows 的值来自一个 HTTP 请求,所以条件的方法似乎有些困难。goroutine 不知道何时广播条件,因为它不知道它要寻找多少行。

英文:

I am trying to wait for a specific condition, and I would like advice as to how this is done best. I have a struct that looks like this (simplified):

type view struct {
    timeFrameReached bool
    Rows []*sitRow
}

In a goroutine, I am updating a file, which is read into the view variable. The number of rows increases, and timeFrameReached will ultimately be true.

Elsewhere, I want to wait for the following condition to be true:

view.timeFrameReached == true || len(view.Rows) >= numRows

I am trying to learn channels and how Go's condition variables work, and I would like to know what is the best solution here. Theoretically, I could do something trivial like this:

for {
    view = getView()
    if view.timeFrameReached == true || len(view.Rows) >= numRows {
        break
    }
}

but that is obviously a naive solution. The value of numRows comes from an HTTP request, so the condition method seems challenging. The goroutine would not know when to broadcast the condition because it wouldn't know the number of rows it is looking for.

答案1

得分: 4

我认为你可以使用条件变量来实现这个功能。这个概念并不是说只有在等待者想要检查的条件为真时才应该执行信号,而是当条件可能为真时(即被检查的事物发生了变化)。

通常的做法是:

mutex.Lock()
for {
    view = getView()
    if view.timeFrameReached == true || len(view.Rows) >= numRows {
        break
    }
    cond.Wait(&mutex)
}
// 处理 view
mutex.Unlock()

然后,在修改 view 的代码中:

mutex.Lock()
// 修改 view
cond.Signal() // 或者 cond.Broadcast()
mutex.Unlock()

显然,我不知道你的程序如何工作,所以你可能需要进行一些修改。

你也可以使用通道来实现类似的功能,通过在通道上发送信号来通知,通过尝试从通道接收来等待,但我觉得这种方法更加复杂。(另外,如果有多个 goroutine 在等待,你可以使用 cond.Broadcast 来唤醒所有的 goroutine。)

英文:

I think I would do this with a condition variable. The concept is not that a Signal should only be done when the condition the waiter wants to check is true, but when it might be true (i.e., the things being checked have changed).

The normal pattern for doing this is:

mutex.Lock()
for {
	view = getView()
	if view.timeFrameReached == true || len(view.Rows) >= numRows {
		break
	}
	cond.Wait(&mutex)
}
// Do stuff with view
mutex.Unlock()

Then, in the code where view is changed:

mutex.Lock()
// Change view
cond.Signal() // or cond.Broadcast()
mutex.Unlock()

Obviously, I've written this without knowing how your program works, so you may have to make some changes.

You could do something similar with a channel by sending on the channel to signal and trying to receive from the channel to wait, but that seems more complicated to me. (Also, if you have more than one goroutine waiting, you can signal all of them to wake up using cond.Broadcast.)

答案2

得分: 1

我有一个想法,涉及通过通道传递所需的行数,构建视图的 goroutine 将进行非阻塞接收,以查看主线程是否请求特定数量的行。如果是这样,它将发送一条消息回来表示条件已满足。

这里的 main 函数请求一定数量的行:

if numRows > len(viewFile.View.Rows) && !viewFile.View.TimeFrameReached {
    // 发送所需的行数
    rows <- numRows
    // 等待预取循环发出视图文件已准备好的信号
    <-rows // 丢弃响应值并继续执行
    view = getView()
}

这里的 goroutine 检查是否需要一定数量的行。如果是这样,它在准备好时回复一个肯定的信号。该信号的值无关紧要。

select {
    case numRows := <-rows:
        if len(viewFile.View.Rows) >= numRows || viewFile.View.TimeFrameReached {
            rows <- 1
        }
    default:
}
英文:

One idea I have involves communicating the needed number of rows via a channel, and the goroutine that is building the view will do a non-blocking receive to see if the main thread is requesting a certain number of rows. If so, it will send a message back to indicate that the condition is met.

Here the main function requests a number of rows:

if numRows &gt; len(viewFile.View.Rows) &amp;&amp; !viewFile.View.TimeFrameReached {
    // Send the required number of rows
	rows &lt;- numRows
	// Wait for the prefetch loop to signal that the view file is ready
	&lt;-rows // Discard the response value and move on
	view = getView()
}

Here the goroutine checks if a certain number of rows are required. If so, it responds with an affirmative signal when ready. The value of that signal is inconsequential.

select {
	case numRows := &lt;-rows:
		if len(viewFile.View.Rows) &gt;= numRows || viewFile.View.TimeFrameReached {
			rows &lt;- 1
		}
	default:
}

huangapple
  • 本文由 发表于 2017年1月20日 05:02:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/41751642.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定