Bat*_*Bat -2 multithreading go busy-waiting
我正在编写Leslie Lamport 的 Bakery 算法的 Go 实现,该算法具有 busy-spin-waits 来处理一些最大数量的线程。
我正在编写一个 go 函数,除非满足特殊条件,否则该函数不应继续。到目前为止,我的代码如下所示:
func acquireLock() {
...
for specialConditionIsFalse {
}
...
}
Run Code Online (Sandbox Code Playgroud)
有没有更有效的方法来停止处理这个线程?
这里有几点值得注意:
goroutine 不是线程。没有“goroutine数量”,系统中goroutine的数量也没有固定的上限。1 Bakery 算法可以修改为处理动态创建的线程(使用列表或地图,如维基百科页面上的 Java 示例),但强烈要求每个“线程”有一个唯一 ID,这使得这不是一个Go 的好主意。(您可以依次使用实现类线程行为的包来解决这个问题,包括线程 ID。)
正如维基百科页面所指出的:
Lamport 的烘焙算法假设了一个顺序一致性内存模型。很少,如果有的话,语言或多核处理器实现这样的内存模型。因此,该算法的正确实现通常需要插入围栏以禁止重新排序。
这意味着您将需要使用该sync/atomic包,这违背了编写自己的锁定的目的。
有了这两个巨大的警告,您可以runtime.Gosched()在您将调用 POSIX 样式yield()函数的地方调用,或者您可以使用通道来表示有人“离开了面包店”,因此轮到下一个用户了。但是通道本身可以完成您需要的所有互斥。一个简化的 Go 特定的非 Lamport 烘焙算法是微不足道的(但以下所有内容都未经测试):
var takeANumber chan int64
var currentlyServing int64
init() {
takeANumber = make(chan int64)
go giveNumbers()
}
// giveNumbers hands out ever-increasing ticket numbers
func giveNumbers() {
for int64 i := 0;; i++ {
takeANumber <- i
}
}
// WaitTurn gets a ticket, then waits until it is our turn. You can
// call this "Lock" if you like.
func WaitTurn() int64 {
ticket := <-takeANumber
for atomic.LoadInt64(¤tlyServing) < ticket {
runtime.Gosched()
}
return ticket
}
// ExitBakery relinquishes our ticket, allowing the next user to proceed.
func ExitBakery(ticket int64) {
atomic.StoreInt64(¤tlyServing, ticket + 1)
}
Run Code Online (Sandbox Code Playgroud)
将其修改为使用两个通道,从而使该WaitTurn功能更高效,留作练习。(当然,除了作为练习之外,一开始就没有理由使用任何这些代码。)
1您可以设置运行时限制,但如果您调用任何阻塞系统调用,系统无论如何都会产生额外的 goroutine。阻塞的系统调用集以及何时被调用取决于运行时,因此您无法真正控制它,至少在不编写特定于平台的代码的情况下无法控制。
| 归档时间: |
|
| 查看次数: |
1181 次 |
| 最近记录: |