对于想了解抛出:所有goroutine都在睡眠中-死锁的读者,本文将提供新的信息,并且为您提供关于golangcoroutine的等待与死锁、Golang中跟踪第三个Goroutine中两个Gorou
对于想了解抛出:所有goroutine都在睡眠中-死锁的读者,本文将提供新的信息,并且为您提供关于golang coroutine 的等待与死锁、Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?、golang 致命错误:所有 goroutine 都处于睡眠状态 - 实现 Lamport 逻辑时钟时出现死锁、Golang::WaitGroup 等待所有goroutine退出的有价值信息。
本文目录一览:- 抛出:所有goroutine都在睡眠中-死锁
- golang coroutine 的等待与死锁
- Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?
- golang 致命错误:所有 goroutine 都处于睡眠状态 - 实现 Lamport 逻辑时钟时出现死锁
- Golang::WaitGroup 等待所有goroutine退出
抛出:所有goroutine都在睡眠中-死锁
给出以下简单的Go程序
package mainimport ( "fmt")func total(ch chan int) { res := 0 for iter := range ch { res += iter } ch <- res}func main() { ch := make(chan int) go total(ch) ch <- 1 ch <- 2 ch <- 3 fmt.Println("Total is ", <-ch)}
我想知道是否有人可以启发我
throw: all goroutines are asleep - deadlock!
谢谢
答案1
小编典典由于您从不关闭ch
通道,因此范围循环将永远不会结束。
您不能在同一频道上发送结果。一种解决方案是使用其他解决方案。
您的程序可以这样修改:
package mainimport ( "fmt")func total(in chan int, out chan int) { res := 0 for iter := range in { res += iter } out <- res // sends back the result}func main() { ch := make(chan int) rch := make(chan int) go total(ch, rch) ch <- 1 ch <- 2 ch <- 3 close (ch) // this will end the loop in the total function result := <- rch // waits for total to give the result fmt.Println("Total is ", result)}
golang coroutine 的等待与死锁
直接上代码:
1. 第一种情况, 如果没有select{}, main 主线程不会等待coroutine运行,导致coroutine得不到机会运行。
You are requesting eventual scheduling (using the two go statements)
of two goroutines and then you exit main without giving the scheduler
a chance to do anything.
有了select, 程序正常运行。
package main import ( "fmt" "time" ) func main() { go func1() go func2() select{} } func func1() { for{ fmt.Println("here1") time.Sleep(10 * time.Minute) } } func func2() { for{ fmt.Println("here2") time.Sleep(10 * time.Minute) }}
2. coroutine有机会运行,但是会发生死锁,Fatal error: all goroutines are asleep - deadlock!
The goroutine executing func1 exited,ditto for func2. The maingoroutine is blocked with no hope to recover while no other goroutinecan be scheduled.
package main import ( "fmt" //"time" ) func main() { go func1() go func2() select { } } func func1() { fmt.Println("here1") } func func2() { fmt.Println("here2") }
3. 第三种情况, 正常运行。
package main import ( "fmt" ) var c = make(chan int,2) func main() { go func1() go func2() <-c <-c fmt.Println("ok") } func func1() { fmt.Println("here1") c <- 1 } func func2() { fmt.Println("here2") c <- 1 }
4. 实现上面的目的的另外一种方法:
var wg sync.WaitGroup var urls = []string{ "http://www.golang.org/","http://www.google.com/","http://www.somestupidname.com/",} for _,url := range urls { // Increment the WaitGroup counter. wg.Add(1) // Launch a goroutine to fetch the URL. go func(url string) { // Decrement the counter when the goroutine completes. defer wg.Done() // Fetch the URL. http.Get(url) }(url) } // Wait for all HTTP fetches to complete. wg.Wait()
Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?
Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么? 在 Golang 中,要跟踪两个 Goroutine 的完成状态并在第三个 Goroutine 中处理它们的结果,最佳实践是使用 sync 包中的 WaitGroup。WaitGroup 允许我们在主 Goroutine 中等待其他 Goroutine 的完成。首先,我们需要创建一个 WaitGroup 对象,并在主 Goroutine 中调用 Add 方法来设置等待的 Goroutine 数量。然后,在每个 Goroutine 的末尾调用 Done 方法来表示该 Goroutine 的完成。最后,在第三个 Goroutine 中调用 Wait 方法来等待所有 Goroutine 的完成。这样,我们就可以安全地跟踪并处理两个 Goroutine 的结果了。这是 Golang 中跟踪多个 Goroutine 完成状态的最佳实践。
问题内容
我有三个并发运行的 goroutine。其中两个进行一些处理并将其结果发送到结果通道。第三个 goroutine 通过读取结果通道来“统计”结果。我可以使用 waitgroup 等待两个计算 goroutine 完成,然后遍历结果通道来统计结果,但这无法扩展,并且需要我创建一个具有巨大缓冲区大小的缓冲结果通道,这是不可接受的在生产代码中。
我想在处理发生时统计结果,但在所有统计完成之前我不想退出程序。在 Go 中实现这一目标的最佳实践是什么?
这是我目前的方法,效果很好。我想知道是否有更好的方法,因为这看起来有点笨拙?
package main import ( "fmt" "sync" ) type T struct{} func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) salesDone := make(chan T) purchasesDone := make(chan T) var wg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) go makeSales(transactions, salesDone) go newPurchases(transactions, purchasesDone) wg.Add(1) go func() { salesAreDone := false purchasesAreDone := false for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } } }() wg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, salesDone chan T) { for i := 0; i < 3000; i++ { transactions <- -100 } salesDone <- struct{}{} } func newPurchases(transactions chan int, purchasesDone chan T) { for i := 0; i < 3000; i++ { transactions <- 100 } purchasesDone <- struct{}{} }
解决方法
不适合任何合理的定义很好。您在这里有一个热门的 for 循环:
立即学习“go语言免费学习笔记(深入)”;
for { select { case transaction := <-transactions: widgetInventory += transaction case <-salesDone: salesAreDone = true case <-purchasesDone: purchasesAreDone = true default: if salesAreDone && purchasesAreDone { wg.Done() return } } }
只要没有通道可供读取,就会执行 default 案例。由于渠道的工作方式,这种情况经常发生。
这个稍作调整的代码版本说明了此循环的“热度”。确切的结果会有所不同,可能会相当高。
Default case ran 27305 times
当 selecting 来自通道时,您不希望出现 default 情况,除非该默认情况也会阻止其中的某些内容。否则你会得到这样的热循环。
更好的方法:使用 nilable 通道进行选择
通常在选择中,您想要识别关闭的通道并将通道变量设置为 nil; select 永远不会成功地从 nil 通道读取内容,因此这实际上“禁用”了该选择。
考虑代码的此修改版本:
go func(transactions chan int, salesDone <-chan T, purchasesDone <-chan T) { defer wg.Done() for transactions != nil { select { case transaction, ok := <-transactions: if ok { widgetInventory += transaction } else { transactions = nil } case <-salesDone: salesDone = nil if purchasesDone == nil { close(transactions) } case <-purchasesDone: purchasesDone = nil if salesDone == nil { close(transactions) } } } }(transactions, salesDone, purchasesDone)
通过对消费者的这些调整,我们不再有热循环;我们总是阻塞直到从通道读取数据。一旦 salesDone 和 purchasesDone 都被“发出信号”,我们 close(transactions)。一旦我们耗尽 transactions 并且它被关闭,我们将 transactions 设置为 nil。我们在 transactions 不为 nil 时循环,在这段代码中,意味着所有通道都是 nil。
微妙但重要的一点:我将通道传递给此函数,因此它的引用不与 main 共享范围。否则,将 transactions 设置为 nil 将写入一个在 goroutine 之间共享的变量。然而在这种情况下,无论如何,这并不重要,因为我们“知道”我们是最后一个从 transactions 读取的内容。
更简单的选项:多个等待组
如果您考虑一下您在这里所做的事情,您需要等到两个生产者都完成对 transactions 的生产。然后你想排空 transactions。一旦通道关闭并排空,main 就知道求和已完成。
您不需要 select 来执行此操作。而 select 为每个“工人”都有一个案例,可以说是相当不优雅的;您必须对多个工作人员进行硬编码并单独处理“完成”通道。
您需要做的是:
- 除了为生产者使用一个 var resultswgsync.WaitGroup 之外,还为消费者添加一个。
- 生产者 defer wg.Done()
- 消费者 defer resultswg.Done() 在遍历 transactions 之前:
go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }()
登录后复制 - main 处理等待生产者、关闭事务以结束范围,然后等待消费者:
wg.Wait() close(transactions) resultswg.Wait()
登录后复制
以这种方式编码,最终会变得简短而甜蜜:
package main import ( "fmt" "sync" ) func main() { var widgetInventory int = 1000 transactions := make(chan int, 100) var wg, resultswg sync.WaitGroup fmt.Println("Starting inventory count = ", widgetInventory) wg.Add(2) go makeSales(transactions, &wg) go newPurchases(transactions, &wg) resultswg.Add(1) go func() { defer resultswg.Done() for transaction := range transactions { widgetInventory += transaction } }() wg.Wait() close(transactions) resultswg.Wait() fmt.Println("Ending inventory count = ", widgetInventory) } func makeSales(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- -100 } } func newPurchases(transactions chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 3000; i++ { transactions <- 100 } }
您可以在这里看到,在此模式中可以有任意数量的生产者;您只需为每个生产者添加 wg.Add(1) 即可。
当我不知道每个工作人员会返回多少结果时,我一直使用这种模式来并行化工作。我发现它很容易理解,并且比尝试 select 多个通道简单得多。事实上,我什至想说,如果您发现自己从多个渠道进行 selecting,您应该退后一步,确保它对您来说确实有意义。我使用 select 的频率远远低于使用等待组的频率。
以上就是Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?的详细内容,更多请关注php中文网其它相关文章!
golang 致命错误:所有 goroutine 都处于睡眠状态 - 实现 Lamport 逻辑时钟时出现死锁
如何解决golang 致命错误:所有 goroutine 都处于睡眠状态 - 实现 Lamport 逻辑时钟时出现死锁?
我正在实现一组代码,在完成向服务器发送消息和向节点广播后打印 Lamport 逻辑时间。在我实现代码 Lamport 逻辑时间之前,我的程序运行良好。打印 closing server...
时,程序中断并显示死锁。我可以知道是否有人可以帮助我发现我的错误吗?
import (
"fmt"
"math/rand"
"time"
)
const num_nodes int = 3
const num_messages int = 2
// some arbitary large number
const buffer_channel = 10000
type Server struct {
serverChannel chan Message
nodeArray []Node
timestamp int
}
type Node struct {
nodeId int
nodeChannel chan Message
server Server
closeChannel chan int
readyChannel chan int
timestamp int
}
type Message struct {
senderId int
messageId int
timestamp int
}
func max(x int,y int) int {
if x > y {
return x
}
return y
}
func broadcast(m Message,s Server) {
for _,n := range s.nodeArray {
if n.nodeId != m.senderId {
broadcastMessage := Message{
m.senderId,m.messageId,s.timestamp,}
go s.broadcastMessage(n.nodeChannel,broadcastMessage)
}
}
}
func (s Server) broadcastMessage(nodeChannel chan Message,broadcastMessage Message) {
fmt.Printf("[Server] is sending Message %d.%d to [Node %d]\n",broadcastMessage.senderId,broadcastMessage.messageId,broadcastMessage.senderId)
nodeChannel <- broadcastMessage
}
func (s Server) listen(messagesBufferChannel chan Message) {
numCompletednodes := 0
for {
nodeMessage := <-s.serverChannel
s.timestamp = max(s.timestamp,nodeMessage.timestamp) + 1
nodeMessage.timestamp = s.timestamp
fmt.Printf("TS: %d -- [Server] has received Message %d.%d from [Node %d]\n",nodeMessage.senderId,nodeMessage.messageId,nodeMessage.senderId)
messagesBufferChannel <- nodeMessage
s.timestamp += 1
broadcast(nodeMessage,s)
if nodeMessage.messageId == num_messages-1 {
numCompletednodes += 1
if numCompletednodes == num_nodes {
fmt.Println("Server finish broadcasting all messages. Stopping Server...")
return
}
}
numMilliSeconds := rand.Intn(1000) + 2000
time.Sleep(time.Duration(numMilliSeconds) * time.Millisecond)
}
}
func (n Node) preSendMessage() {
for i := 1; i <= num_messages; i++ {
numMilliSeconds := rand.Intn(1000) + 2000
time.Sleep(time.Duration(numMilliSeconds) * time.Millisecond)
n.readyChannel <- i
}
}
func (n Node) listenSendMessages(messagesBufferChannel chan Message) {
for {
select {
case receivedMessage := <-n.nodeChannel:
n.timestamp = max(n.timestamp,receivedMessage.timestamp) + 1
receivedMessage.timestamp = n.timestamp
fmt.Printf("TS: %d -- [Node %d] has received Message %d.%d from [Server]\n",n.timestamp,n.nodeId,receivedMessage.senderId,receivedMessage.messageId)
messagesBufferChannel <- receivedMessage
case nodeMessageId := <-n.readyChannel:
n.timestamp += 1
fmt.Printf("TS: %d -- [Node %d] is sending Message %d.%d to [Server]\n",nodeMessageId)
nodeMessage := Message{
n.nodeId,nodeMessageId,}
n.server.serverChannel <- nodeMessage
case <-n.closeChannel:
fmt.Printf("Stopping [node %d]\n",n.nodeId)
return
default:
}
}
}
func main() {
fmt.Println("Start of Program...")
server := Server{
serverChannel: make(chan Message),nodeArray: []Node{},timestamp: 0,}
for i := 1; i <= num_nodes; i++ {
newNode := Node{
nodeId: i,nodeChannel: make(chan Message),server: server,readyChannel: make(chan int),closeChannel: make(chan int),timestamp: 0,}
server.nodeArray = append(server.nodeArray,newNode)
}
var messagesBufferChannel chan Message = make(chan Message,buffer_channel)
for _,n := range server.nodeArray {
go n.preSendMessage()
go n.listenSendMessages(messagesBufferChannel)
}
server.listen(messagesBufferChannel)
time.Sleep(time.Second)
for _,n := range server.nodeArray {
n.closeChannel <- 1
}
time.Sleep(time.Second)
close(messagesBufferChannel)
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)
Golang::WaitGroup 等待所有goroutine退出
简介
waitgroup类似于的信号量,用于等待所有的goroutine退出,基本的操作包括
add(int) 增加信号的值,add的参数应该都为正数 ,done() 减少信号的值,相当于add(-1) ,wait() 等待信号量值为0,等待期间会一直阻塞
code
package main import ( "fmt" "sync" "time" ) var waitGroutp = sync.WaitGroup{} func goroutine1() { time.Sleep(time.Second * 3) waitGroutp.Done() fmt.Println("goroutine1退出") } func goroutine2() { time.Sleep(time.Second * 8) waitGroutp.Done() fmt.Println("goroutine2退出") } func goroutine3() { time.Sleep(time.Second * 1) waitGroutp.Done() fmt.Println("goroutine3退出") } func goroutine4() { time.Sleep(time.Second * 2) waitGroutp.Done() fmt.Println("goroutine4退出") } func main() { fmt.Println("waitgroup测试") go goroutine1() go goroutine2() go goroutine3() go goroutine4() waitGroutp.Add(4) //waitGroutp.Add(5) //add 大于开goroutie数目会死锁, //waitGroutp.Add(2) //少了会panic sync: negative WaitGroup counter waitGroutp.Wait() time.Sleep(time.Second * 2) fmt.Println("所有goroutine已经退出") }
今天关于抛出:所有goroutine都在睡眠中-死锁的介绍到此结束,谢谢您的阅读,有关golang coroutine 的等待与死锁、Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?、golang 致命错误:所有 goroutine 都处于睡眠状态 - 实现 Lamport 逻辑时钟时出现死锁、Golang::WaitGroup 等待所有goroutine退出等更多相关知识的信息可以在本站进行查询。
本文标签: