如果您对为什么在同一goroutine中使用未缓冲的通道会导致死锁?感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于为什么在同一goroutine中使用未缓冲的通道会导致死锁
如果您对为什么在同一goroutine中使用未缓冲的通道会导致死锁?感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于为什么在同一goroutine中使用未缓冲的通道会导致死锁?的详细内容,并且为您提供关于c# – 为什么WPF的Dispatcher.Invoke在主线程上运行时不会导致死锁?、goroutine 通过waitgroup 确保 goroutine执行完成,select+time.After+chanel 终止goroutine等待、Goroutine是协作调度的。这是否意味着不执行的goroutine将导致goroutine逐一运行?、Goroutine是协作调度的这是否意味着不执行的goroutine将导致goroutine逐一运行?的有价值信息。
本文目录一览:- 为什么在同一goroutine中使用未缓冲的通道会导致死锁?
- c# – 为什么WPF的Dispatcher.Invoke在主线程上运行时不会导致死锁?
- goroutine 通过waitgroup 确保 goroutine执行完成,select+time.After+chanel 终止goroutine等待
- Goroutine是协作调度的。这是否意味着不执行的goroutine将导致goroutine逐一运行?
- Goroutine是协作调度的这是否意味着不执行的goroutine将导致goroutine逐一运行?
为什么在同一goroutine中使用未缓冲的通道会导致死锁?
我敢肯定对这种琐碎的情况有一个简单的解释,但是我对go
并发模型是陌生的。
当我运行这个例子
package mainimport "fmt"func main() { c := make(chan int) c <- 1 fmt.Println(<-c)}
我收到此错误:
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:main.main() /home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52exit status 2
为什么呢
包装c <-
成一个goroutine
使示例按预期运行
package mainimport "fmt"func main() { c := make(chan int) go func(){ c <- 1 }() fmt.Println(<-c)}
再次,为什么?
请,我需要深入的解释,而不仅仅是如何消除死锁并修复代码。
答案1
小编典典从文档中:
如果通道未缓冲,则发送方将阻塞,直到接收方收到该值为止。如果通道具有缓冲区,则发送方仅阻塞该值,直到将值复制到该缓冲区为止;否则,发送方才阻塞。如果缓冲区已满,则意味着要等到某些接收器检索到一个值。
否则说:
- 当频道已满时,发送方等待另一个goroutine通过接收来腾出空间
- 您会看到一个未缓冲的通道总是一个完整的通道:必须有另一个goroutine来处理发送方发送的内容。
这条线
c <- 1
阻塞,因为通道没有缓冲。由于没有其他goroutine可以接收该值,因此情况无法解决,这是一个僵局。
您可以将频道创建更改为
c := make(chan int, 1)
这样,频道在被阻止之前就可以容纳一个项目。
但这不是并发的意义所在。通常,没有其他goroutine不会使用通道来处理放入内部的内容。您可以这样定义一个接收goroutine:
func main() { c := make(chan int) go func() { fmt.Println("received:", <-c) }() c <- 1 }
示范
c# – 为什么WPF的Dispatcher.Invoke在主线程上运行时不会导致死锁?
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void button_Click(object sender,RoutedEventArgs e) { //System.Threading.Thread.CurrentThread = button.dispatcher.Thread button.dispatcher.Invoke(() => button.Content = "1234"); } }
当然,button_Click在主线程上运行.
我的理解是button.dispatcher.Thread是主线程,只有当线程没有被阻塞时才会处理Invoke().但是,在这种情况下,主线程是否被阻止?即主线程正在等待dispatcher.Invoke()调用完成,dispatcher.Invoke()正在等待主线程释放.所以我希望在这里遇到僵局,但它不会陷入僵局.
为什么?
P.S:我知道在这种情况下我不需要dispatcher.Invoke,我可以直接调用button.Content =“1234”.我试图理解为什么在这种情况下不会发生死锁.
解决方法
“好吧,Invoke会阻塞调用线程,直到操作完成.如果线程被阻塞,它如何在线程上执行操作?”
如果我们查看源代码,我们会看到回调不仅在同一个线程上调用,而且直接在Invoke方法内调用.主线程没有被阻止.
如果查看调度程序的Reference Source页面,您可以在Invoke方法的实现中看到if语句上面的以下注释,并在其中调用回调:
// Fast-Path: if on the same thread,and invoking at Send priority,// and the cancellation token is not already canceled,then just // call the callback directly. if(!cancellationToken.IsCancellationRequested && priority == dispatcherPriority.Send && CheckAccess()) { /* snipped */ callback(); /* snipped */ }
您在主线程上调用dispatcher.Invoke,该方法只需立即调用它即可处理.
*嗯,不是直接的,但Invoke(Action)的整个主体只是对上面代码所在方法的调用.
goroutine 通过waitgroup 确保 goroutine执行完成,select+time.After+chanel 终止goroutine等待
package main
import (
"fmt"
"log"
"sync"
"time"
)
func main() {
tasks := make(chan bool, 1)
go func(t chan bool){
var wg sync.WaitGroup
// 需要确保3个协程完成
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(ii int) {
defer panicRecover()
if err := testa(ii); err != nil {
t <- false
}
wg.Done()
}(i)
}
wg.Wait()
t <- true
}(tasks)
// 限时120s等待协程完毕
select {
case <-time.After(time.Second * 120):
fmt.Println("处理超时")
case flag := <-tasks:
if !flag {
fmt.Println("多协程任务中有失败的")
}else {
log.Println("协程正确完成任务")
}
}
fmt.Printf("over")
}
func testa(i int) error {
time.Sleep(time.Second * 3)
fmt.Println(i)
if i == 3 {
return fmt.Errorf("i can''t be 3")
}
return nil
}
func panicRecover(){
if err := recover(); err != nil {
log.Printf("recover from panic: %v", err)
}
}
Goroutine是协作调度的。这是否意味着不执行的goroutine将导致goroutine逐一运行?
来自:http :
//blog.nindalf.com/how-goroutines-work/
由于goroutine是协同调度的,因此不断循环的goroutine会使同一线程上的其他goroutine饿死。
Goroutine很便宜,并且如果被阻塞,不会导致对其进行复用的线程阻塞
- 网络输入
- 睡眠
- 渠道运营或
- 阻塞sync包中的原语。
因此,鉴于以上所述,假设您有一些类似这样的代码,除了循环随机次数并打印总和外,什么也不做:
func sum(x int) { sum := 0 for i := 0; i < x; i++ { sum += i } fmt.Println(sum)}
如果您使用goroutines之类的
go sum(100)go sum(200)go sum(300)go sum(400)
如果只有一个线程,goroutine将一一运行吗?
答案1
小编典典克里克所有评论的汇编和整理。
抢占式意味着内核(运行时)允许线程运行特定时间,然后在其他线程不做任何事情或一无所知的情况下将其执行。在OS内核中,通常使用硬件中断来实现。进程不能阻止整个操作系统。在协作式多任务处理中,线程必须显式地让他人执行。如果不是这样,它可能会阻塞整个过程甚至整个机器。Go就是这样做的。它在goroutine可以产生执行的一些非常特定的点。但是,如果goroutine仅针对{}执行,那么它将锁定整个过程。
但是,引用中没有提及运行时中的最新更改。fmt.Println(sum)
可能导致其他goroutine被调度,因为较新的运行时将在函数调用时调用调度程序。
如果您没有任何函数调用,只需进行一些数学运算,那么是的,goroutine将锁定线程,直到其退出或命中可能使他人执行的东西。这就是为什么for{}
在Go中不起作用。更糟糕的是,由于GO的工作原理,即使GOMAXPROCS>
1,它仍然会导致进程挂起,但是无论如何您都不应依赖于此。很高兴了解这些东西,但不要指望它。甚至有建议在像您这样的循环中插入调度程序调用
Go的运行时所做的主要事情是尽其所能,使每个人都可以执行而不会饿死任何人。语言规范中未对此进行规定,并且将来可能会更改。如果关于循环的提议将被实施,那么即使没有函数调用也可能发生切换。目前,您唯一需要记住的是,在某些情况下,函数调用可能导致goroutine产生执行。
为了解释Akavall的答案中的切换,当fmt.Printf
被调用时,它要做的第一件事是检查是否需要增加堆栈并调用调度程序。它可能会切换到另一个goroutine。是否切换取决于其他goroutine的状态以及调度程序的确切实现。像任何调度程序一样,它可能会检查是否有饥饿的goroutines应该执行。在许多迭代中,函数调用有更大的机会进行切换,因为其他人的饥饿时间更长。经过几次迭代,goroutine在饥饿发生之前就完成了。
Goroutine是协作调度的这是否意味着不执行的goroutine将导致goroutine逐一运行?
来自:http :
//blog.nindalf.com/how-goroutines-work/
由于goroutine是协同调度的,因此不断循环的goroutine会使同一线程上的其他goroutine饿死。
Goroutine很便宜,并且如果被阻塞,不会导致对其进行复用的线程阻塞
- 网络输入
- 睡眠
- 渠道运营或
- 阻塞sync包中的原语。
因此,鉴于以上所述,假设您有一些类似这样的代码,除了循环随机次数并打印总和外,什么也不做:
func sum(x int) {
sum := 0
for i := 0; i < x; i++ {
sum += i
}
fmt.Println(sum)
}
如果您使用goroutines之类的
go sum(100)
go sum(200)
go sum(300)
go sum(400)
如果只有一个线程,goroutine将一一运行吗?
今天的关于为什么在同一goroutine中使用未缓冲的通道会导致死锁?的分享已经结束,谢谢您的关注,如果想了解更多关于c# – 为什么WPF的Dispatcher.Invoke在主线程上运行时不会导致死锁?、goroutine 通过waitgroup 确保 goroutine执行完成,select+time.After+chanel 终止goroutine等待、Goroutine是协作调度的。这是否意味着不执行的goroutine将导致goroutine逐一运行?、Goroutine是协作调度的这是否意味着不执行的goroutine将导致goroutine逐一运行?的相关知识,请在本站进行查询。
本文标签: