Java转Go学习之旅 | Go入门(2)

入门

    • 获取一个 URL
    • 并发获取多个 URL

获取一个 URL

获取每个指定 URL 的内容,然后不加解析地输出:

// 输出从URL获取的内容  
package main  
  
import (  
   "fmt"  
   "io"   "net/http"   "os")  
  
func main() {  
   for _, url := range os.Args[1:] {  
      // 创建 HTTP 请求  
      resp, err := http.Get(url)  
      if err != nil {  
         fmt.Fprintf(os.Stderr, "fetch: %v\n", err)  
         os.Exit(1)  
      }  
  
      // 打印状态码  
      fmt.Println(resp.Status)  
  
      b, err := io.ReadAll(resp.Body)  
      resp.Body.Close()  
      if err != nil {  
         fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)  
         os.Exit(1)  
      }  
      fmt.Printf("%s", b)  
   }  
}
  • http.Get函数是创建 HTTP 请求的函数,如果获取过程没有出错,那么会在resp这个结构体中得到访问的请求结果
  • respBody字段包括一个可读的服务器响应流
  • io.ReadAll函数从response中读取到全部内容,并将其结果保存在变量 b 中
  • resp.Body.Close关闭respBody流,防止资源泄露

并发获取多个 URL

Go 语言最有意思并且最新奇的特性就是对并发编程的支持

// 并发获取URL并报告它们的时间和大小  
package main  
  
import (  
   "fmt"  
   "io"   "io/ioutil"   "net/http"   "os"   "time")  
  
func main() {  
   start := time.Now()  
   ch := make(chan string)  
   for _, url := range os.Args[1:] {  
      go fetch(url, ch) // 启动一个goroutine  
   }  
   for range os.Args[1:] {  
      fmt.Println(<-ch) // 从通道ch接收  
   }  
   fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())  
}  
  
func fetch(url string, ch chan<- string) {  
   start := time.Now()  
   resp, err := http.Get(url)  
   if err != nil {  
      ch <- fmt.Sprint(err) // 发送到通道ch  
      return  
   }  
   nbytes, err := io.Copy(ioutil.Discard, resp.Body)  
   resp.Body.Close() // 不要泄露资源  
   if err != nil {  
      ch <- fmt.Sprintf("while reading %s: %v", url, err)  
      return  
   }  
   secs := time.Since(start).Seconds()  
   ch <- fmt.Sprintf("%.2fs  %7d  %s", secs, nbytes, url)  
}
  • goroutine 是一种函数的并发执行方式,而 channel 是用来在 goroutine 之间进行参数传递
  • main 函数本身也运行在一个 goroutine 中,而go function则表示创建一个新的 goroutine,并在这个新的 goroutine 中执行这个函数
  • main 函数中用make函数创建了一个传递string类型参数的 channel,对每一个命令行参数,我们都用go这个关键字来创建一个 goroutine,并且让函数在这个 goroutine 异步执行http.Get方法

通道 ch 是一个同步的通道,也就是说,当一个 goroutine 向它发送数据时,它会阻塞,直到另一个 goroutine 从它接收数据为止。这样就可以保证,每一个 goroutine 都能将它的结果发送到通道 ch,而不会被其他 goroutine 覆盖或丢失。同时,通道 ch 也可以保证,main 函数中的循环能够按照 goroutine 发送的顺序,依次从通道 ch 接收数据,而不会错乱或重复。因此,只需要一个通道 ch,就能实现并发获取 URL 并报告它们的时间和大小。

通道 ch 不会影响总的执行时间,因为它只是用于传递数据,而不是执行任务。通道 ch只有在发送或接收数据时才会阻塞,而不是在整个goroutine的运行过程中

因此,通道 ch 的阻塞时间可以忽略不计,或者认为是包含在 goroutine 的执行时间中的。例如,假设有两个 goroutine,它们的执行时间分别是 2 秒和 3 秒,但是它们都需要向通道 ch 发送数据,而通道 ch 的容量只有 1,那么第二个 goroutine 就需要等待第一个 goroutine 传递完数据后才能发送。假设这样就会产生 0.1 秒的阻塞时间。但是,这个阻塞时间可以认为是第二个 goroutine 的执行时间的一部分,因为它是在执行任务的过程中发生的,即第二个 goroutine 的执行时间仍然是 3 秒。因此,总的执行时间还是 3 秒,而不是 3.1 秒。

你可能感兴趣的:(Go语言之旅,java,golang,学习)