Java程序员学习golang六小时后,竟然深夜

    科技2022-07-12  134

    写在前面

           竟然大半夜不睡觉写博客。这个标题,是不是像一些无良卖课吸睛公众号,点开一看味同嚼蜡。


          好吧,大半夜码字,因为实在激动。从下午六点多开始学习golang,花了三个小时了解变量、指针、数据类型、结构体、语法、函数以及golang的切片、管道、(go程)协程等特性,用半小时思考我该写个啥,花了一个半小时写了个小白入门爬虫,最后傻乐了半小时。

    文章目录

    写在前面前言一、Golang是什么?1.简介 二、使用步骤1.下载镜像2.下载GoLang(别的也行,VSCode)3.开干 三、核心特性1.goroutine协程1.1 什么是 Go 协程?1.2 goroutine和线程的比较1.3 goroutine协程实现原理 2.channel2.1 什么是 channel?2.2 咋用? 四、小小爬虫1.爬虫四步2.代码3.结果 总结


    前言

          我是写Java的,所以本文是浅尝辄止golang后一得之见,才疏学浅,希望对想了解golang的小伙伴有所帮助。

    一、Golang是什么?

    1.简介

    官话:golang是Google开发的一种 静态强类型、编译型,并发型,并具有垃圾回收功能的编程语言。

    个人看法:

    有很多C语言的影子,比如语法、控制流结构、基础数据类型、传参、指针等,方便上手(Java写习惯了,顺手扔出来的分号,是那么的格格不入无处安放)能访问底层操作系统,还提供了强大的网络编程和并发编程支持(channel和Goroutine是核心)

    二、使用步骤

    1.下载镜像

    镜像地址

    下载对应的版本就行

    2.下载GoLang(别的也行,VSCode)

    链接在此

    3.开干

    个人途径

    官方文档:文档一号,文档二号视频:视频在此

    三、核心特性

    1.goroutine协程

         大道至简

    1.1 什么是 Go 协程?

    先理解进程、线程 之间概念的区别

    对于 进程、线程,都是有内核进行调度,有 CPU 时间片的概念,进行 抢占式调度(有多种调度算法)

    协程是用户级线程,对内核是透明的,系统并不知道有协程的存在。完全由用户自己的程序进行调度,通常只能进行协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到。

    goroutine 和协程区别

    本质上,goroutine 就是协程。 不同的是,Golang 在 runtime、系统调用等多方面对 goroutine调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前 goroutine 的CPU 转让出去,让其他goroutine 能被调度并执行。从语言层面原生支持协程,在函数或者方法前面加 go关键字就可创建一个协程。

    1.2 goroutine和线程的比较

    内存消耗切换调度开销每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少。goroutine:2KB 线程:8MBgoroutine 远比线程小;线程:涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP…等寄存器的刷新等。goroutine:只有三个寄存器的值修改 - PC / SP / DX.

    1.3 goroutine协程实现原理

    协程是基于线程的。内部实现上,维护了一组数据结构和 n 个线程,真正的执行还是线程,协程执行的代码被扔进一个待执行队列中,由这 n 个线程从队列中拉出来执行。这就解决了协程的执行问题。 暂时只了解到这里,待续


    2.channel

    2.1 什么是 channel?

    channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,有点类似于Linux中的管道。

    2.2 咋用?

    1.声明channel 2.引用类型 3.单向channel var 变量名 chan 数据类型 channel和和map类似,channel也一个对应make创建的底层数据结构的引用。 当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是nil。定义一个channel时,也需要定义发送到channel的值的类型。 // 方法一:channel的创建赋值 var ch chan int; ch = make(chan int); // 方法二:短写法 ch:=make(chan int); // 方法三:综合写法:全局写法!!!! var ch = make(chan int); 单向chan //定义只读的channel read_only := make (<-chan int) //定义只写的channel write_only := make (chan<- int)

    四、小小爬虫

    1.爬虫四步

    踩点(找目标)http://www.netbian.com/fengjing/index_2.htm爬(拿数据)过滤(获取想要的)数据处理

    2.代码

    package main import ( "fmt" "io/ioutil" "net/http" "regexp" "strconv" "strings" "sync" "time" ) var ( //存放图片的管道 chanImageUrls chan string wg sync.WaitGroup //监控协程 chanTask chan string ) func main() { chanImageUrls = make(chan string, 1000000) chanTask = make(chan string, 26) for i := 2; i < 10; i++ { wg.Add(1) go getImageUrls("http://www.netbian.com/fengjing/index_" + strconv.Itoa(i) + ".htm") } wg.Add(1) go checkOK() //下载 for i := 0; i < 3; i++ { wg.Add(1) go downloadImg() } wg.Wait() } //下载文件 func downLoadFile(url string, filename string) (ok bool) { resp, err := http.Get(url) HandleError(err, "http.get.url") defer resp.Body.Close() bytes, err := ioutil.ReadAll(resp.Body) HandleError(err, "res.Body") filename = "D:/test/" + filename err = ioutil.WriteFile(filename, bytes, 0666) if err != nil { return false } else { return true } } //下载图片 func downloadImg() { for url := range chanImageUrls { filename := getFilenameFromUrl(url) ok := downLoadFile(url, filename) if ok { fmt.Println(filename + "下载成功") } else { fmt.Println(filename + "下载失败") } } } //截取名字 func getFilenameFromUrl(url string) (filename string) { lastIndex := strings.LastIndex(url, "/") filename = url[lastIndex+1:] timePrefix := strconv.Itoa(int(time.Now().UnixNano())) filename = timePrefix + "_" + filename return } //任务统计协程 func checkOK() { var count int for { url := <-chanTask fmt.Println("完成爬取 " + url) count++ if count == 26 { close(chanImageUrls) break } } wg.Done() } //爬取图片链接到管道 func getImageUrls(url string) { urls := getImages(url) for _, url := range urls { chanImageUrls <- url } chanTask <- url wg.Done() } //获取当前页面图片链接 func getImages(url string) (urls []string) { pageStr := getResByPage(url) re := regexp.MustCompile(reImage) result := re.FindAllStringSubmatch(pageStr, -1) for _, res := range result { url := res[0] urls = append(urls, url) } return urls }

    3.结果


    总结

    睡觉

    Processed: 0.015, SQL: 8