Go 并发编程

文章目录

  • 用 goroutine 和通道实现并发
  • 用 sync 实现并发
    • 互斥锁
    • sync.Once 结构体
    • 同步等待组 zync.WaitGroup
    • 竞态检测器
  • 应用
    • 自增整数生成器
    • 并发消息发送器
    • 多路复合计算器
    • 用 select 关键字创建多通道监听器
      • 多路复合计算器
      • 超时处理
    • 用无缓冲通道阻塞主线程
    • 用筛法求素数
    • 创建随机数生成器
    • 创建一个定时器
    • Go Web爬虫

用 goroutine 和通道实现并发

package mainimport ("fmt"
)func main() {ch := make(chan string) // 构建一个通道go func() {             // 开启一个并发匿名函数fmt.Println("开始协程") // 通过通道通知main的协程ch <- "signal"fmt.Println("退出协程")}()fmt.Println("等待协程")<-ch // 等待匿名协程fmt.Println("完成")
}
package mainimport "fmt"func main() {// 这里我们定义了一个可以存储整数类型的带缓冲通道,缓冲区大小为3ch := make(chan int, 3)// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据// 而不用立刻需要去同步读取数据ch <- 6ch <- 7ch <- 8// 获取这三个数据fmt.Println(<-ch)fmt.Println(<-ch)fmt.Println(<-ch)
}
package mainimport ("fmt"
)func fibonacci(n int, ch chan int) {a, b := 0, 1for i := 0; i < n; i++ {ch <- aa, b = b, a+b}close(ch)
}func main() {ch := make(chan int, 6)go fibonacci(cap(ch), ch)for j := range ch {fmt.Println(j)}
}

用 sync 实现并发

互斥锁

package mainimport ("fmt""sync""time"
)func main() {var mutex sync.Mutexwait := sync.WaitGroup{}fmt.Println("Locked")mutex.Lock()for i := 1; i <= 5; i++ {wait.Add(1)go func(i int) {fmt.Println("Not lock:", i)mutex.Lock()fmt.Println("Lock:", i)time.Sleep(time.Second)fmt.Println("Unlock:", i)mutex.Unlock()defer wait.Done()}(i)}time.Sleep(time.Second)fmt.Println("Unlocked")mutex.Unlock()wait.Wait()
}

sync.Once 结构体

package mainimport ("fmt""sync"
)func main() {var once sync.OnceonceBody := func() {fmt.Println("test only once,这里只打印一次!")//打印}done := make(chan bool)for i := 0; i < 6; i++ {go func() {once.Do(onceBody)//确保只背执行一次done <- true}()}for i := 0; i < 6; i++ {<-done}
}

同步等待组 zync.WaitGroup

package mainimport ("fmt""sync""time"
)func main() {var wg sync.WaitGroupwg.Add(1)go func() {defer wg.Done()fmt.Println("1 goroutine sleep ...")time.Sleep(2)fmt.Println("1 goroutine exit ...")}()wg.Add(1)go func() {defer wg.Done()fmt.Println("2 goroutine sleep ...")time.Sleep(4)fmt.Println("2 goroutine exit ...")}()fmt.Println("Waiting for all goroutine ")wg.Wait()fmt.Println("All goroutines finished!")
}
package mainimport ("fmt""sync""time"
)func main() {testFunc := func(wg *sync.WaitGroup, id int) {defer wg.Done()fmt.Printf("%v goroutine start ...\n", id)time.Sleep(2)fmt.Printf("%v goroutine exit ...\n", id)}var wg sync.WaitGroupconst N = 3wg.Add(N)for i := 0; i < N; i++ {go testFunc(&wg, i)}fmt.Println("Waiting for all goroutine")wg.Wait()fmt.Println("All goroutines finished!")
}

竞态检测器

  • go run/build/test -race main.go
  • 模拟非法竞态访问数据
package mainimport "fmt"func main() {c := make(chan bool)m := make(map[string]string)go func() {m["a"] = "one" // 第一个冲突访问.c <- true}()m["b"] = "two" // 第一个冲突访问<-cfor k, v := range m {fmt.Println(k, v)}
}

应用

自增整数生成器

package mainimport "fmt"// 生成自增的整数
func IntegerGenerator() chan int {var ch chan int = make(chan int)// 开启 goroutinego func() {for i := 0; ; i++ {ch <- i // 直到通道索要数据,才把i添加进信道}}()return ch
}func main() {generator := IntegerGenerator()for i := 0; i < 100; i++ { //生成100个自增的整数fmt.Println(<-generator)}
}

并发消息发送器

package mainimport "fmt"func SendNotification(user string) chan string {//......此处省略查询数据库获取新消息。//声明一个通道来保存消息notifications := make(chan string, 500)// 开启一个通道go func() {//将消息放入通道notifications <- fmt.Sprintf("Hi %s, welcome to our site!", user)}()return notifications
}func main() {barry := SendNotification("barry")     //  获取barry的消息shirdon := SendNotification("shirdon") // 获取shirdon的消息// 获取消息的返回fmt.Println(<-barry)fmt.Println(<-shirdon)
}

多路复合计算器

package mainimport ("fmt""math/rand""time"
)// 这里可以是比较耗时的事情,比如计算
func doCompute(x int) int {time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) //模拟计算return 1 + x                                                // 假如1 + x是一个很费时的计算
}// 每个分支开出一个goroutine做计算,并把计算结果发送到各自通道
func branch(x int) chan int {ch := make(chan int)go func() {ch <- doCompute(x)}()return ch
}func Recombination(chs ...chan int) chan int {ch := make(chan int)for _, c := range chs {// 注意此处明确传值go func(c chan int) { ch <- <-c }(c) // 复合}return ch
}func main() {//返回复合结果result := Recombination(branch(10), branch(20), branch(30))for i := 0; i < 3; i++ {fmt.Println(<-result)}
}

用 select 关键字创建多通道监听器

package mainimport ("fmt"
)func foo(i int) chan int {ch := make(chan int)go func() { ch <- i }()return ch
}func main() {ch1, ch2, ch3 := foo(3), foo(6), foo(9)ch := make(chan int)// 开一个goroutine监视各个通道数据输出并收集数据到通道chgo func() {for {// 监视ch1, ch2, ch3的流出,并全部流入通道chselect {case v1 := <-ch1:ch <- v1case v2 := <-ch2:ch <- v2case v3 := <-ch3:ch <- v3}}}()// 阻塞主线,取出通道ch的数据for i := 0; i < 3; i++ {fmt.Println(<-ch)}}

多路复合计算器

package mainimport ("fmt""math/rand""time"
)//这里可以是比较耗时的事情,比如计算
func doCompute(x int) int {time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) //模拟计算return 1 + x                                                // 假如1 + x是一个很费时的计算
}// 每个分支开出一个goroutine做计算,并把计算结果发送到各自通道
func branch(x int) chan int {ch := make(chan int)go func() {ch <- doCompute(x)}()return ch
}func Recombination(branches ... chan int) chan int {ch := make(chan int)//select会尝试着依次取出各个通道的数据go func() {for i := 0; i < len(branches); i++ {select {case v1 := <-branches[i]:ch <- v1}}}()return ch
}func main() {//返回复合结果result := Recombination(branch(10), branch(20), branch(30))for i := 0; i < 3; i++ {fmt.Println(<-result)}
}

超时处理

			//timeout 是一个计时通道, 如果达到时间了,就会发一个信号出来timeout := time.After(1 * time.Second)for isTimeout := false; !isTimeout; {select { // 监视通道ch1, ch2, ch3, timeout通道的数据流出case v1 := <-ch1:fmt.Printf("received %d from ch1", v1)case v2 := <-ch2:fmt.Printf("received %d from ch2", v2)case v3 := <-ch3:fmt.Printf("received %d from ch3", v3)case <-timeout:isTimeout = true // 超时}}

用无缓冲通道阻塞主线程

package mainimport ("fmt"
)func main() {ch, quit := make(chan int), make(chan int)go func() {ch <- 8   // 添加数据quit <- 1 // 发送完成信号}()for isQuit := false; !isQuit; {// 监视通道ch的数据流出select {case v := <-ch:fmt.Printf("received %d from ch", v)case <-quit:isQuit = true // quit通道有输出,关闭for循环}}
}

用筛法求素数

package mainimport "fmt"//生成自增的整数
func IntegerGenerator() chan int {var ch chan int = make(chan int)go func() { // 开出一个goroutinefor i := 2; ; i++ {ch <- i  // 直到通道索要数据,才把i添加进通道}}()return ch
}func Filter(in chan int, number int) chan int {// 输入一个整数队列,筛出是number倍数的, 不是number的倍数的放入输出队列// in:  输入队列out := make(chan int)go func() {for {i := <-in // 从输入中取一个if i%number != 0 {out <- i // 放入输出通道}}}()return out
}func main() {const max = 100               // 找出100以内的所有素数numbers := IntegerGenerator() // 初始化一个整数生成器number := <-numbers           // 从生成器中抓一个整数(2), 作为初始化整数for number <= max { // number作为筛子,当筛子超过max的时候结束筛选fmt.Println(number)               // 打印素数, 筛子即一个素数numbers = Filter(numbers, number) //筛掉number的倍数number = <-numbers                // 更新筛子}
}

创建随机数生成器

package mainimport "fmt"func randGenerator() chan int {ch := make(chan int)go func() {for {//select会尝试执行各个case, 如果都可以执行,那么随机选一个执行select {case ch <- 0:case ch <- 1:}}}()return ch
}func main() {//初始化一个随机生成器generator := randGenerator()//测试,打印10个随机数for i := 0; i < 10; i++ {fmt.Println(<-generator)}
}

创建一个定时器

package mainimport ("fmt""time"
)func Timer(duration time.Duration) chan bool {ch := make(chan bool)go func() {time.Sleep(duration)// 到时间啦ch <- true}()return ch}func main() {// 定时5秒timeout := Timer(5 * time.Second)for {select {case <-timeout:// 到时fmt.Println("already 5s!")//结束程序return}}
}

Go Web爬虫

package mainimport ("fmt""io""net/http""os""strconv"
)func Get(url string) (result string, err error) {resp, err := http.Get(url) // 修改此行if err != nil {return "", err}defer resp.Body.Close()// 读取网页的body内容buf := make([]byte, 4*1024)for {n, err := resp.Body.Read(buf)if err != nil {if err == io.EOF {fmt.Println("文件读取完毕")break} else {fmt.Println("resp.Body.Read err = ", err)break}}result += string(buf[:n])}return result, nil // 修改此行
}// 将所有的网页内容爬取下来
func SpiderPage(i int, page chan<- int) {url := "https://github.com/search?q=go&type=Repositories&p=1" + strconv.Itoa((i-1)*50)fmt.Printf("正在爬取第%d个网页\n", i)//爬,将所有的网页内容爬取下来result, err := Get(url)if err != nil {fmt.Println("http.Get err = ", err)return}//把内容写入到文件filename := "page" + strconv.Itoa(i) + ".html"f, err := os.Create(filename)if err != nil {fmt.Println("os.Create err = ", err)return}//写内容f.WriteString(result)//关闭文件f.Close()//每爬完一个,就给个值page <- i
}func Run(start, end int) {fmt.Printf("正在爬取第%d页到%d页\n", start, end)//因为很有可能爬虫还没有结束下面的循环就已经结束了,所以这里就需要且到通道page := make(chan int)for i := start; i <= end; i++ {//将page阻塞go SpiderPage(i, page)}for i := start; i <= end; i++ {fmt.Printf("第%d个页面爬取完成\n", <-page) //这里直接将面码传给点位符,值直接从管道里取出}
}func main() {var start, end intfmt.Printf("请输入起始页数字>=1:> ")fmt.Scan(&start)fmt.Printf("请输入结束页数字:> ")fmt.Scan(&end)Run(start, end)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/122151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Security漏洞防护—HttpFirewall和 HTTPS

一、HttpFirewall Spring Security有几个领域&#xff0c;你所定义的 pattern 会针对传入的请求进行测试&#xff0c;以决定应该如何处理请求。这发生在 FilterChainProxy 决定请求应该通过哪个过滤链时&#xff0c;以及 FilterSecurityInterceptor 决定哪些安全约束适用于请求…

阿里云/腾讯云国际站代理:国际腾讯云的优势

国际腾讯云具有以下优势&#xff1a; 1. 全球覆盖&#xff1a;腾讯云在全球拥有30个区域&#xff0c;覆盖6个大洲&#xff0c;能够提供全球范围的云服务&#xff0c;满足不同地区用户的需求。 2. 大规模网络&#xff1a;腾讯云拥有庞大的全球网络&#xff0c;包括多个高速骨干…

Go 语言操作 MongoDb

文章目录 连接数据库插入数据库插入一条数据批量插入数据 查询数据用 BSON 进行复合查询聚合查询 更新数据删除数据 连接数据库 package mainimport ("context""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options"…

计算机毕设 flink大数据淘宝用户行为数据实时分析与可视化

文章目录 0 前言1、环境准备1.1 flink 下载相关 jar 包1.2 生成 kafka 数据1.3 开发前的三个小 tip 2、flink-sql 客户端编写运行 sql2.1 创建 kafka 数据源表2.2 指标统计&#xff1a;每小时成交量2.2.1 创建 es 结果表&#xff0c; 存放每小时的成交量2.2.2 执行 sql &#x…

Servlet 与Spring对比!

前言&#xff1a; Spring相关的框架知识&#xff0c;算是目前公司在用的前沿知识了&#xff0c;很重要&#xff01;&#xff01; 那么以Spring为基础的框架有几个&#xff1f; 以Spring为基础的框架包括若干模块&#xff0c;其中主要的有Spring Framework、Spring Boot、Spring…

十七、模型构建器(ModelBuilder)快速提取城市建成区——批量将夜光数据投影、转为整型(基于参考比较法)

一、前言 本文以参考比较法提取城市建成区为例,详细介绍如何使用模型构建器(ModelBuilder)提高效率,特别说明一下,模型构建器构建的模型是可以保存成为工具,日后需要使用的时候可以直接使用工具不用重复构建,因为模型构建器构建的工作流如果不保存,下次就不能使用,需…

Mybatis基础

文章目录 Mybatis基础XML语言概述使用Mybatis配置Mybatis增删改查复杂查询事务操作动态 SQLifchoose、when、otherwise 缓存机制注解开发 Mybatis基础 虽然我们能够通过JDBC来连接和操作数据库&#xff0c;但是哪怕只是完成一个SQL语句的执行&#xff0c;都需要编写大量的代码…

【Python入门教程】基于OpenCV视频分解成图片+图片组合成视频(视频抽帧组帧)

在人工智能爆火的今天&#xff0c;深度学习被广泛应用于各个领域。深度学习的模型训练离不开大量的样本库。我之前分享过【Python爬虫】批量爬取网页的图片&制作数据集&#xff0c;今天跟大家分享一下如何使用OpenCV库对视频进行抽帧&#xff0c;从而增加样本图片的数量。正…

Operator开发之operator-sdk入门

1 operator-sdk 除了kubebuilder&#xff0c;operator-sdk是另一个常用的用于开发Operator的框架&#xff0c;不过operator-sdk还是基于kubebuilder&#xff0c;因此&#xff0c;通常还是建议使用kubebuilder开发Operator。 2 环境准备 跟kubebuilder类似&#xff0c;需要安…

Tensorflow2 中模型训练标签顺序和预测结果标签顺序不一致问题解决办法

本篇文章将详细介绍Tensorflow2.x中模型训练标签顺序和预测结果标签顺序不一致问题&#xff0c;这个问题如果考虑不周&#xff0c;或者标签顺序没有控制好的情况下会出现预测结果精度极其不准确的情况。 训练数据集的结构&#xff1a;数据集有超过10的类别数&#xff0c;这里包…

【Java 进阶篇】Java HTTP 请求消息详解

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是一种用于传输超文本的应用层协议&#xff0c;广泛用于构建互联网应用。在Java中&#xff0c;我们经常需要发送HTTP请求来与远程服务器进行通信。本文将详细介绍Java中HTTP请求消息的各个部分&#xff0c;包括请求行、…

shouldComponentUpdate 是做什么的?

目录 前言 生命周期函数 shouldComponentUpdate 的写法和用法 代码 事件和API 优缺点 方法 总结 理论 结论 shouldComponentUpdate 是 React 类组件中的一个生命周期方法&#xff0c;用于决定一个组件的 props 或 state 发生变化时是否应该重新渲染。默认情况下&…

什么是离岸金融 (OFFSHORE FINANCE)

什么是离岸金融 离岸金融&#xff08;Offshore Finance&#xff09;是指在国外或离岸地区开设金融账户、进行金融交易或管理金融资产的金融实践。这通常涉及将资金、投资、银行账户或金融交易放置在国外的特殊地区或国家&#xff0c;以获得各种金融和税收优惠。离岸金融的目的…

设计模式--7个原则

单一职责原则&#xff1a;一个类负责一项职责。 里氏替换原则&#xff1a;继承与派生的规则。 依赖倒置原则&#xff1a;高层模块不应该依赖基层模块&#xff0c;二者都应该依赖其抽象&#xff1b;抽象不应该依赖细节&#xff1b;细节应该依赖抽象。即针对接口编程&#xff0…

Linux系统下DHCP服务安装部署和使用实例详解(蜜罐)

目录 一、概述 二、具体配置如下&#xff1a; 一、概述 DHCP &#xff1a;动态主机设置协议&#xff08;英语&#xff1a;Dynamic Host Configuration Protocol&#xff0c;DHCP&#xff09;是一个局域网的网络协议&#xff0c;使用UDP协议工作&#xff0c;主要有两个用途&…

公司电脑如何限制安装软件

公司电脑如何限制安装软件 安企神终端管理系统下载使用 在企业环境中&#xff0c;电脑已经成为企业中必不可少的办公工具&#xff0c;确保员工的生产力和公司的信息安全是至关重要的。为了实现这一目标&#xff0c;公司可能会限制员工在某些情况下安装软件或者由管理员来为终…

可以实时监控屏幕的电脑监控软件

电脑已经成为了人们工作和生活不可或缺的工具。然而&#xff0c;这也带来了诸多安全问题。一些人可能会利用电脑进行不恰当的操作&#xff0c;如聊天、游戏、观看视频等&#xff0c;甚至会泄露公司的商业机密。 电脑监控软件的定义 电脑监控软件是一种用于监控电脑使用情况的软…

【Docker】Docker的网络

Docker提供了多种内置的网络模式&#xff0c;用于在容器之间建立网络连接。这些网络模式&#xff0c;包括桥接网络、主机网络、无网络模式。我们将主要探讨每种网络模式的优缺点、适用场景。 桥接网络 桥接网络是Docker的默认网络模式。在桥接网络中&#xff0c;Docker会为每…

数字音频工作站软件 Ableton Live 11 mac中文软件特点与功能

Ableton Live 11 mac是一款数字音频工作站软件&#xff0c;用于音乐制作、录音、混音和现场演出。它由Ableton公司开发&#xff0c;是一款极其流行的音乐制作软件之一。 Ableton Live 11 mac软件特点和功能 Comping功能&#xff1a;Live 11增加了Comping功能&#xff0c;允许用…

PHP:json_encode和json_decode用法

json_encode 函数用于将 PHP 数据结构转换为 JSON 字符串。json_decode 函数用于将 JSON 字符串转换为 PHP 数据结构。 // 将 PHP 数据结构转换为 JSON 字符串 $data ["name" > "John","age" > 25,"city" > "New York&…