go并发编程基础

这篇具有很好参考价值的文章主要介绍了go并发编程基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

go并发编程

1waitgroup

WaitGroup就是等待所有的goroutine全部执行完毕,add方式和Down方法要配套使用

package main

import (
	"fmt"
	"sync"
)

func main()  {
	var wq sync.WaitGroup

	wq.Add(100) //监控多少个goroutine执行结束

	for i:= 0;i<100;i++ {
        //开启一个协程
		go func(i int) {
			defer wq.Done() //和add是一起使用的
			fmt.Println(i)
		}(i)
	}

	wq.Wait() //等待所有的goroutine结束
}

2通过锁来完成全局变量的原子性操作

开启两个gorountine对total进行相同此时的加减,但是这一段程序的运行结果每一次都不一样

资源竞争,加锁

package main

import (
	"fmt"
	"sync"
)

var total int
var wg sync.WaitGroup

func main() {
	wg.Add(2)
	go add()
	go sub()
	wg.Wait()
	fmt.Println(total)
}
func add() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		total += 1
	}
}

func sub() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		total -= 1
	}
}

加锁之后代码成功运行

package main

import (
	"fmt"
	"sync"
)

var total int
var wg sync.WaitGroup
var lock sync.Mutex

func main() {
	wg.Add(2)
	go add()
	go sub()
	wg.Wait()
	fmt.Println(total)
}
func add() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		lock.Lock()
		total += 1
		lock.Unlock()
	}
}

func sub() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		lock.Lock()
		total -= 1
		lock.Unlock()
	}
}

锁不能复制。

更加优雅的方式,使用golang的原子包

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var total int64
var wg sync.WaitGroup

//var lock sync.Mutex

func main() {
	wg.Add(2)
	go add()
	go sub()
	wg.Wait()
	fmt.Println(total)
}
func add() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {

		atomic.AddInt64(&total, 1)  //原子性的操作

	}
}

func sub() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		atomic.AddInt64(&total,-1)
	}
}

lock相对atomic性能较差,lock基于操作系统调度

3读写锁

锁实际上是将并行的代码串行化了,使用lock肯定影响性能,即使是设计所,也应该尽量保证并行

4goroutine进行通讯

不要通过共享内存来通讯,而要通过通讯来实现内存共享

channel的基础用法

package main

import "fmt"

func main()  {
	// 名字  类型  存储类型
	var msg chan string //默认值未nil

	msg = make(chan string ,1)  //channel的初始化的值如果是0的话,放值进去会阻塞,如果设为0就为无缓冲channel

	msg <- "大大怪" //将右边的值放在channel中
	name := <- msg //将channel中的值取出来给name
	fmt.Println(name)

}


无缓冲channel用法

package main

import (
	"fmt"
	"time"
)

func main() {
	// 名字  类型  存储类型
	var msg chan string //默认值未nil

	msg = make(chan string, 0) //channel的初始化的值如果是0的话,放值进去会阻塞,如果设为0就为无缓冲channel
	go func(msg chan string) {
		name := <-msg //将channel中的值取出来给name
		fmt.Println(name)
	}(msg)
	msg <- "大大怪"  //将右边的值放在channel中

time.Sleep(time.Second*10)

}

waitgroup少了一个done容易出现deadlock

无缓冲的channel也容易出现deadlock

适用场景

无缓冲channel适用于通知B要第一时间知道A是否已经完成

有缓冲channel适用于生产者和消费者之间的通讯

go中channel的应用场景

  • 消息传递,消息过滤
  • 信号广播
  • 事件订阅和广播
  • 任务分发
  • 结果汇总
  • 并发控制
  • 同步和异步

5.单项channel的使用

默认情况下,channel是双向的,但是我们经常一个channel作为参数进行传递,希望对象也是单向使用

package main

import "fmt"

func main() {
	//var ch1 chan int //双向的channel
	//var ch2 chan<- float64 //单项channel,只能写入float64的数据
	//var ch3 <-chan int //只能读取int类型的数据

	/*
	定义一个channel然后把它编程单向的,但是不能把单项的channel转成双向的channel
	 */
	c := make(chan int, 3)
	var send chan<- int = c
	var read <-chan int = c

	send <- 1
	num := <- read

	fmt.Println(num)
}

模拟单项channel存取数据

package main

import (
	"fmt"
	"time"
)

func producer(out chan<- int)  {
	for i:=0;i<10 ;i++  {
		out <- i*i
	}
	close(out)
}

func consumer(in <-chan int)  {
	for num := range in {
		fmt.Println(num)
	}
}
func main() {
	/*
	内部会完成自动的类型转换
	 */
	c := make(chan int)
	go producer(c)
	go consumer(c)
	time.Sleep(10*time.Second)
}

6交替打印

这是一道经典题目,在Java中也有提到,交替打印这个序列

12ab34cd56ef78gh910ij1112kl1314mn1516op1718qr1920st2122uv2324wx2526yz2728

利用channel阻塞的特性来实现文章来源地址https://www.toymoban.com/news/detail-674383.html

package main

import (
	"fmt"
	"time"
)

var number, letter = make(chan bool), make(chan bool)

func printNum() {
	i := 1
	for {
		//等待另外一个goroutine进行通知
		<-number //从number进行取值的操作,如果没有值就阻塞
		fmt.Printf("%d%d", i, i+1)
		i += 2
		letter <- true
	}
}
func printLetter() {
	str := "abcdefghijklmnopqrstuvwxyz"
	i := 0
	for {
		<-letter
		if i>= len(str){
			return
		}
			fmt.Print(str[i : i+2])
		i += 2
		number <- true // 存入true到channel中
	}
}

func main() {
	go printNum()
	go printLetter()
	number <- true
	time.Sleep(10*time.Second)
}

到了这里,关于go并发编程基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GO语言网络编程(并发编程)select

    1.1.1 select多路复用 在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现: 这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,G

    2024年02月09日
    浏览(75)
  • 【Go】Go语言并发编程:原理、实践与优化

    在当今的计算机世界,多核处理器和并发编程已经成为提高程序执行效率的关键。Go语言作为一门极富创新性的编程语言,凭借其强大的并发能力,在这方面表现出色。本文将深入探讨Go语言并发编程的原理,通过实际代码示例展示其应用,并讨论可能的优化策略。 在了解G

    2024年02月10日
    浏览(55)
  • Go语言并发编程(千锋教育)

    视频地址:https://www.bilibili.com/video/BV1t541147Bc?p=14 作者B站:https://space.bilibili.com/353694001 源代码:https://github.com/rubyhan1314/go_goroutine 1.1、并发与并行 其实操作系统里对这些概念都有所说明和举例。 并发 并发是指多个任务在同一时间段内交替执行,从外部看似乎是同时执行的。

    2024年02月14日
    浏览(56)
  • Go 语言面试题(三):并发编程

    对于无缓冲的 channel,发送方将阻塞该信道,直到接收方从该信道接收到数据为止,而接收方也将阻塞该信道,直到发送方将数据发送到该信道中为止。 对于有缓存的 channel,发送方在没有空插槽(缓冲区使用完)的情况下阻塞,而接收方在信道为空的情况下阻塞。 例如: 协

    2024年02月08日
    浏览(48)
  • go并发编程基础

    WaitGroup就是等待所有的goroutine全部执行完毕,add方式和Down方法要配套使用 开启两个gorountine对total进行相同此时的加减,但是这一段程序的运行结果每一次都不一样 资源竞争,加锁 加锁之后代码成功运行 锁不能复制。 更加优雅的方式,使用golang的原子包 lock相对atomic性能较

    2024年02月11日
    浏览(43)
  • GO语言网络编程(并发编程)runtime包

    1.1.1. runtime.Gosched() 让出CPU时间片,重新等待安排任务(大概意思就是本来计划的好好的周末出去烧烤,但是你妈让你去相亲,两种情况第一就是你相亲速度非常快,见面就黄不耽误你继续烧烤,第二种情况就是你相亲速度特别慢,见面就是你侬我侬的,耽误了烧烤,但是还馋就

    2024年02月09日
    浏览(62)
  • Go语言(Golang)数据库编程

    要想连接到 SQL 数据库,首先需要加载目标数据库的驱动,驱动里面包含着于该数据库交互的逻辑。 sql.Open() 数据库驱动的名称 数据源名称 得到一个指向 sql.DB 这个 struct 的指针 sql.DB 是用来操作数据库的,它代表了0个或者多个底层连接的池,这些连接由sql 包来维护,sql 包会

    2024年02月03日
    浏览(86)
  • 掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)

    通道(Channel)是用来在 Go 程序中传递数据的一种数据结构。它是一种类型安全的、并发安全的、阻塞式的数据传输方式,用于在不同的 Go 协程之间传递消息。 基本概念 创建通道 :使用 make() 函数创建一个通道。 发送数据 :使用 - 操作符向通道发送数据。 接收数据 :使用

    2024年03月21日
    浏览(57)
  • 云原生时代崛起的编程语言Go并发编程实战

    @ 目录 概述 基础理论 并发原语 协程-Goroutine 通道-Channel 多路复用-Select 通道使用 超时-Timeout 非阻塞通道操作 关闭通道 通道迭代 定时器-TimerAndTicker 工作池-Worker Pools 等待组-WaitGroup 原子操作-Atomic 互斥锁-Mutex 读写互斥锁-RWMutex 有状态协程 单执行-Once 条件-Cond 上下文-Context 信

    2024年02月02日
    浏览(53)
  • Go 语言并发编程 及 进阶与依赖管理

    协程可以理解为 轻量级线程 ; Go更适 合高并发场景原因 之一: Go语言 一次可以创建上万协成 ; “快速”: 开多个协成 打印。 go func() : 在 函数前加 go 代表 创建协程 ; time.Sleep() : 协程阻塞,使主协程 在 子协程结束前阻塞不退出 ; 乱序输出 说明并行 ; 通过通信共享内

    2024年02月13日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包