当前位置: 首页 > news >正文

Go并发编程

一、goroutine 和 通道

在Go语言中,每一个并发执行的活动成为goroutine。通道则是每一个goroutine之间传递消息的工具。

1、Goroutine

在一个Go程序中,只有一个主Goroutine来调用main函数。生成新的goroutine也十分简单,例如有一个函数:f(), 只需在其前面加上go关键字即可将其作为并发程序执行。
例:

package mainimport ("fmt""time"
)func main() {n := 5//开启goroutinego f(n)for i := 0; i < n; i++ {fmt.Println("I am goroutine main()")time.Sleep(50 * time.Millisecond)}
}func f(n int) {for i := 0; i < n; i++ {fmt.Println("I am goroutine f()")time.Sleep(50 * time.Millisecond)}
}// sout:
// I am goroutine main()
// I am goroutine f()
// I am goroutine f()
// I am goroutine main()
// I am goroutine main()
// I am goroutine f()
// I am goroutine f()
// I am goroutine main()
// I am goroutine main()
// I am goroutine f()

虽然Go语言实现并发十分方便,但是如何实现goroutine之间的通信以及保证并发的安全性依然是一件有挑战的事。

2、通道(chan)

通道是goroutine之间的连接,让特定的值在各个goroutine之间传递。
使用make函数创建一个通道:ch := make(chan type, cap). type为ch传递值的类型,cap为通道的大小(同时能容纳多少值),type可以为任意类型。
通道对应操作(都存在阻塞):

  1. 发送 ( c h < − x ch<-x ch<x) x为对应类型的变量,ch为通道。(ch满了会存在阻塞)
  2. 接受( x < − c h x<-ch x<ch)x为对应类型的变量,ch为通道。(ch空了会存在阻塞)
  3. 关闭通道close(ch)(ch发送操作被禁止,接受操作被释放)

无缓冲通道: 通道只有一个元素,接受方和发送方会相互阻塞。
管道: 通道有多个chan元素,用来连接不同的goroutine,一个chan的输入是另一个chan的输入。
单向通道类型: 类型( c h a n < − i n t chan<-int chan<int)为只能发送的通道,类型( < − c h a n i n t <-chan int <chanint)为只能接收的int类型通道。
缓冲通道: 有一个元素队列,一个cap大于1的通道类型。

注意细节:

  1. chan变量默认值为nil,使用’=='时若二者是同一通道数据的引用时返回true。
  2. 若关闭后还有写操作会发生宕机panic,读取其零值故可将其作为开关使用。
  3. 操作 x,ok := <-ch (x为读取的值,ok为bool值,当ch被close时会返回false,故可以使用range来获取ch的值直到ch被close)。
  4. select开关操作(关闭某一服务),利用select捕获通道关闭的信息,从而关闭对应的服务。
    例:
	done := make(chan struct{})go func() {os.Stdin.Read(make([]byte, 1))close(done)}()loop:for {select {//捕获是否关闭。case <-done:fmt.Println("program end!")break loopdefault:fmt.Println("program runing!")}time.Sleep(1 * time.Second)}

一个爬虫案例,使用bfs方式爬取一个网页的所有url:

package gorunimport ("fmt""log""net/http""golang.org/x/net/html"
)func Catch() {//通道用来在goroutine之间传递结果worklist := make(chan []string)//计数器用来判断结束状态,关闭goroutinecnt := 0go func() {worklist <- []string{"http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/person/person_home.html"}}()cnt++//标记已遍历的url,防止重复的url被读取seen := make(map[string]bool)for ; cnt > 0; cnt-- {list := <-worklistfor _, link := range list {if !seen[link] {seen[link] = truecnt++//慢函数,用新的goroutine执行,提高效率。go func(link string) {//该处会不停地生成新的goroutineworklist <- crawl(link)}(link)}}}
}
//chan变量,利用其阻塞的特性,用于控制新增goroutine的数量
var token = make(chan struct{}, 20)// 在生成goroutine时做限制,控制生成的goroutine的数量
func crawl(url string) []string {fmt.Println(url)//获取token <- struct{}{}list, err := extract(url)//释放defer func() {<-token}()if err != nil {log.Println(err)}return list
}//分析网页中的html文件,搜索其中的url
func extract(url string) ([]string, error) {resp, err := http.Get(url)if err != nil {return nil, err}if resp.StatusCode != http.StatusOK {resp.Body.Close()return nil, fmt.Errorf("getting %s: %s", url, resp.Status)}doc, err := html.Parse(resp.Body)resp.Body.Close()if err != nil {return nil, fmt.Errorf("parse %s as html: %v", url, err)}var links []stringvisitNode := func(n *html.Node) {if n.Type == html.ElementNode && n.Data == "a" {for _, a := range n.Attr {if a.Key != "href" {continue}link, err := resp.Request.URL.Parse(a.Val)if err != nil {continue}links = append(links, link.String())}}}forEachNode(doc, visitNode, nil)return links, nil
}func forEachNode(n *html.Node, pre, post func(n *html.Node)) {if pre != nil {pre(n)}for c := n.FirstChild; c != nil; c = c.NextSibling {forEachNode(c, pre, post)}if post != nil {post(n)}
}
http://www.lryc.cn/news/217773.html

相关文章:

  • Nignx及负载均衡动静分离
  • HDFS架构介绍
  • 微信小程序提示确认框 wx.showModal
  • 如何设置OBS虚拟摄像头给钉钉视频会议使用
  • SpringCloud 微服务全栈体系(十一)
  • 45基于matlab的ARIMA:AutoregressiveIntegratedMovingAverage model。
  • 2010年408计网
  • 初谈Linux-Linux环境搭建(阿里云免费服务器+xshell)
  • 如何利用AppScan扫描H5页面,进行安全测试?
  • Oracle数据库中的table@xyz是什么意思?
  • springboot常见网络相关错误及原因解析
  • 【C语言_线程pthread_互斥锁mutex_条件触发cond 之解析与示例 (开源)】.md updata:23/11/03
  • mongodb如何删除数据并释放空间
  • k8s之集群调度
  • 代码随想录算法训练营第四十二天丨 动态规划part05
  • [css] flex 子元素自动撑开父元素宽度
  • 全新干货!一招教你迅速提升流量主收入!包你轻松月入过万
  • 连接两个dataframe
  • 【入门Flink】- 05Flink运行时架构以及一些核心概念
  • 网络协议的基本概念
  • 广汽传祺E9上市,3DCAT实时云渲染助力线上3D高清看车体验
  • resource manager attributes structure(iofunc_attr_t) 扩展实例
  • 劳易测扫码条码分段读取实现方法
  • 【Linux】Nignx及负载均衡动静分离
  • AI:50-基于深度学习的柑橘类水果分类
  • mysql 中!= 到底走不走索引?
  • 4 sql语法基础
  • 网络工程师应知应会:基础知识(5)
  • Minio多节点多驱动分布式部署官网文档翻译
  • python连接clickhouse (CK)