GVKun编程网logo

为什么在同一goroutine中使用未缓冲的通道会导致死锁?

14

如果您对为什么在同一goroutine中使用未缓冲的通道会导致死锁?感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于为什么在同一goroutine中使用未缓冲的通道会导致死锁

如果您对为什么在同一goroutine中使用未缓冲的通道会导致死锁?感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于为什么在同一goroutine中使用未缓冲的通道会导致死锁?的详细内容,并且为您提供关于c# – 为什么WPF的Dispatcher.Invoke在主线程上运行时不会导致死锁?、goroutine 通过waitgroup 确保 goroutine执行完成,select+time.After+chanel 终止goroutine等待、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在主线程上运行时不会导致死锁?

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等待

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逐一运行?

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逐一运行?

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逐一运行?的相关知识,请在本站进行查询。

本文标签: