GoLang并发-主例程从未被调用

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

GoLang concurrency- Main routine is never called

问题

我是一个对golang不熟悉的新手。正在尝试学习并发编程。目前,我对使用通道在分叉后进行连接的概念感到困惑。以下是我目前的代码:

package main

import (
	"fmt"
	"net/http"

	"github.com/vijay-psg587/golang/golang-srv/src/modules/test-dir-DONT-USE/concurrency/status-checker/models"
)

func printChannelData(str string, url chan models.URLCheckerModel) {
	
	fmt.Println("通道中的数据为:", str)
	resp, err := http.Get(str)
	if err != nil {

		url <- models.URLCheckerModel{
			StatusCode: 500,
			URL:        str,
			Message:    err.Error(),
		}

	} else {
		

		url <- models.URLCheckerModel{
			StatusCode: 200,
			URL:        str,
			Message:    resp.Status,
		}
	}

}

func main() {
	fmt.Println("主函数开始...")

	links := []string{"http://google.com", "http://golang.org", "http://amazon.com"}

	op := make(chan models.URLCheckerModel)
	defer close(op)
	for i := 0; i < len(links); i++ {
		// 调用协程

		go func(str string) {
			printChannelData(str, op)
		}(links[i])

		

	}

	for data := range op {

		fmt.Println("接收到的数据:", data)
	}

	fmt.Println("主函数结束...")
}

当我执行这段代码时,我的go函数不会结束。它会获取数据,但从不返回到主go例程。

现在,如果我在连接点进行以下更改:

// for data := range op {

// 	fmt.Println("接收到的数据:", data)
// }
fmt.Println("接收到的数据:", <-op)
fmt.Println("接收到的数据:", <-op)
fmt.Println("接收到的数据:", <-op)

而不是使用for循环,那么我可以获取数据,并且主go例程也会结束。不确定我在哪里犯了错误,以及为什么主例程从未被调用。

英文:

I am a newbie to golang. Trying to learn concurrency. The concept with the join point after forking using the channels troubles me now. THis is the code that I have in place

package main

import (
	&quot;fmt&quot;
	&quot;net/http&quot;

	&quot;github.com/vijay-psg587/golang/golang-srv/src/modules/test-dir-DONT-USE/concurrency/status-checker/models&quot;
)

func printChannelData(str string, url chan models.URLCheckerModel) {
	
	fmt.Println(&quot;Data in channel is: &quot;, str)
	resp, err := http.Get(str)
	if err != nil {

		url &lt;- models.URLCheckerModel{
			StatusCode: 500,
			URL:        str,
			Message:    err.Error(),
		}

	} else {
		

		url &lt;- models.URLCheckerModel{
			StatusCode: 200,
			URL:        str,
			Message:    resp.Status,
		}
	}

}

func main() {
	fmt.Println(&quot;Main started...&quot;)
	

	links := []string{&quot;http://google.com&quot;, &quot;http://golang.org&quot;, &quot;http://amazon.com&quot;}

	op := make(chan models.URLCheckerModel)
	defer close(op)
	for i := 0; i &lt; len(links); i++ {
		// call to goroutine

		go func(str string) {
			printChannelData(str, op)
		}(links[i])

		

	}

	for data := range op {

		fmt.Println(&quot;data receveied:&quot;, data)
	}

	fmt.Println(&quot;Main ended...&quot;)
}

Now when I execute this, my go function does NOT end. It gets the data

Main started...
Data in channel is:  http://amazon.com
Data in channel is:  http://google.com
Data in channel is:  http://golang.org

data receveied: {200 http://google.com 200 OK}

data receveied: {200 http://golang.org 200 OK}

data receveied: {200 http://amazon.com 200 OK}

Three go routines kick off and run, but it never ends. Doesn't come back to the main go routine at all

Now if I make the change like this, (in the join point)

// for data := range op {

	// 	fmt.Println(&quot;data receveied:&quot;, data)
	// }
	fmt.Println(&quot;data received:&quot;, &lt;-op)
	fmt.Println(&quot;data received:&quot;, &lt;-op)
	fmt.Println(&quot;data received:&quot;, &lt;-op)

instead of the for loop, then I get the data and the main go routine also ends. Not sure where I am making the mistake and why the main routine is never called

Main started...
Data in channel is:  http://amazon.com
Data in channel is:  http://golang.org
Data in channel is:  http://google.com
data received: {200 http://google.com 200 OK}
data received: {200 http://golang.org 200 OK}
data received: {200 http://amazon.com 200 OK}
Main ended...

答案1

得分: 1

在Go语言中,当使用for range遍历通道时,迭代器会一直阻塞,直到底层通道关闭。

x := make(chan int, 2)
x <- 1
x <- 2
close(x) // 如果没有这行代码,下面的循环将永远不会停止

for i := range x {
  print(i)
}

在你的情况下,使用defer意味着通道只有在父方法退出后才会关闭(即循环结束后),这会导致死锁。

通常,在使用扇出/扇入模型时,你会使用类似于sync.WaitGroup的东西来协调关闭通道/继续执行。

在你的情况下,可能会像下面这样:

links := []string{"http://google.com", "http://golang.org", "http://amazon.com"}

op := make(chan models.URLCheckerModel)
var wg sync.WaitGroup

for i := 0; i < len(links); i++ {
    wg.Add(1)
    go func(str string) {
        defer wg.Done()
        printChannelData(str, op)
    }(links[i])
}

go func() {
  wg.Wait()
  close(op)
}()

for data := range op {
    fmt.Println("data receveied:", data)
}
英文:

When using for range over a channel in Go, the iterator will continue to block until the underlying channel is closed.

x := make(chan int, 2)
x &lt;- 1
x &lt;- 2
close(x) // Without this, the following loop will never stop

for i := range x {
  print(i)
}

In your case, using defer means that the channel will only be closed once the parent method exits (i.e. after the loop finishes), which leads to a deadlock.

Usually, when using a fan-out/fan-in model, you'll use something like the sync.WaitGroup to coordinate closing the channel/continuing.

In your case, that would likely look something like the following:

links := []string{&quot;http://google.com&quot;, &quot;http://golang.org&quot;, &quot;http://amazon.com&quot;}

op := make(chan models.URLCheckerModel)
var wg sync.WaitGroup

for i := 0; i &lt; len(links); i++ {
    wg.Add(1)
    go func(str string) {
        defer wg.Done()
        printChannelData(str, op)
    }(links[i])
}

go func() {
  wg.Wait()
  close(op)
}()

for data := range op {
    fmt.Println(&quot;data receveied:&quot;, data)
}

答案2

得分: 1

你的通道是无缓冲的,因此,你在main.go中的代码将无限运行,等待某些事件发生,因为代码无法继续执行,无缓冲通道是同步的,任何监听该通道的人只有在接收到值时才会停止监听(在这种情况下,由于请求可能需要一些时间才能到达或永远不会到达,代码将在main.go中无限运行)。

你有两个选择:

  • 或者为你的通道定义缓冲区,大小为你的URL列表的大小,并使你的代码真正异步和并发。

         op := make(chan models.URLCheckerModel, len(links))
    
  • 或者你将不得不创建多个通道,每个URL一个,这样当每个URL被处理时,值就会被加载,并且你在search方法中关闭你的通道。

      links := []string{"http://google.com", "http://golang.org", "http://amazon.com"}
    
      for i := 0; i < len(links); i++ {
         // 调用goroutine
         op := make(chan models.URLCheckerModel)
    
         go func(str string) {
      	  printChannelData(str, op)
    
      	  fmt.Println(<-op)
         }(links[i])
     }
    
    
    
    
     func printChannelData(str string, url chan models.URLCheckerModel) {
        fmt.Println("通道中的数据是:", str)
        resp, err := http.Get(str)
        if err != nil {
            url <- models.URLCheckerModel{
      	      StatusCode: 500,
      	      URL:        str,
      	      Message:    err.Error(),
           }
       } else {
           url <- models.URLCheckerModel{
      	     StatusCode: 200,
      	     URL:        str,
      	     Message:    resp.Status,
          }
      }
    
         close(url)
      }
    
英文:

Your channel is unbuffered and so, your code in main.go will be infinitely running, waiting for something to happen, because the code cannot continue since unbuffered channels are synchronous, where anyone who is listening on that channel can only stop listen when it receives a value (in this case, as it can sometimes take time for the request to arrive or never arrive, the code will be infinitely running inside main.go).

You have two options:

  • Or you define buffers for your channel to be the size of your list of
    URL's and make your code really asynchronous and concurrent.

        op := make(chan models.URLCheckerModel, len(links))
    
  • Or you will have to create several channels, one for each URL, so
    that when each one is processed, the value is loaded and you CLOSE
    your channel within the search method.

     links := []string{&quot;http://google.com&quot;, &quot;http://golang.org&quot;, &quot;http://amazon.com&quot;}
    
     for i := 0; i &lt; len(links); i++ {
        // call to goroutine
        op := make(chan models.URLCheckerModel)
    
        go func(str string) {
     	  printChannelData(str, op)
    
     	  fmt.Println(&lt;-op)
        }(links[i])
    }
    
    
    
    func printChannelData(str string, url chan models.URLCheckerModel) {
       fmt.Println(&quot;Data in channel is: &quot;, str)
       resp, err := http.Get(str)
       if err != nil {
           url &lt;- models.URLCheckerModel{
     	      StatusCode: 500,
     	      URL:        str,
     	      Message:    err.Error(),
          }
      } else {
          url &lt;- models.URLCheckerModel{
     	     StatusCode: 200,
     	     URL:        str,
     	     Message:    resp.Status,
         }
     }
    
        close(url)
     }
    

huangapple
  • 本文由 发表于 2022年4月28日 22:24:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/72045426.html
匿名

发表评论

匿名网友

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

确定