Go-Task12-并行编程

本文介绍了Go语言中的并发编程概念,包括并发与并行的区别,为何需要并发,以及Go语言的并发原语。重点讨论了Goroutines和Channels,包括Goroutines的使用、WaitGroup、Once、Mutex、Cond、原子操作和Pool。同时,详细阐述了无缓冲与有缓冲的Channels,以及如何使用Select进行通信选择。
摘要由CSDN通过智能技术生成


打卡结束了, 但Go的学习真的完结了吗?

1.并发编程

1.1 并发与并行

Erlang 之父 Joe Armstrong曾经以下图解释并发与并行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5gHSbAUe-1608972337948)(https://github.com/datawhalechina/go-talent/raw/master/img/cor.jpg)]

并发在图中的解释是两队人排队接咖啡,两队切换。

并行是两个咖啡机,两队人同时接咖啡。

“Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.” — Rob Pike

并发使并行变得容易,并发提供了一种构造解决方案的方法,并行一般伴随这多核。并发一般伴随这CPU切换轮训。

1.2 为什么需要并发?

原因有很多,其中比较重要的原因如下:

  1. 不阻塞等待其他任务的执行,从而浪费时间,影响系统性能。
  2. 并行可以使系统变得简单些,将复杂的大任务切换成许多小任务执行,单独测试。

在开发中,经常会遇到为什么某些进程通常会相互等待呢?为什么有些运行慢,有些快呢?

通常受限来源于进程I/OCPU

  • 进程I/O限制

如:等待网络或磁盘访问

  • CPU限制

如:大量计算

1.3 Go并发原语

1.3.1 协程Goroutines

每个go程序至少都有一个Goroutine:主Goroutine(在运行进程时自动创建)。以及程序中其他Goroutine 例如:下面程序创建了main的Goroutine及匿名的Goroutine。

func main() {
   
	go func() {
   
		fmt.Println("you forgot me !")
	}()
}

在go中有个package是sync,里面包含了:

WaitGroup、Mutex、Cond、Once、Pool,下面依次介绍。

1.WaitGroup

假设主线程要等待其余的goroutine都运行完毕,不得不在末尾添加time.Sleep(),但是这样会引发两个问题:

  • 等待多长时间?
  • 时间太长,影响性能?

在go的sync库中的WaitGroup可以帮助我们完成此项工作,Add(n)把计数器设置为n,Done()会将计数器每次减1,Wait()函数会阻塞代码运行,直到计数器减0。

等待多个goroutine完成,可以使用一个等待组。 例如:

// 这是我们将在每个goroutine中运行的函数。
// 注意,等待组必须通过指针传递给函数。
func worker(id int, wg *sync.WaitGroup) {
   

	defer wg.Done()

	fmt.Printf("Worker %d starting\n", id)

	time.Sleep(time.Second)
	fmt.Printf("Worker %d done\n", id)
}

func main() {
   

	var wg sync.WaitGroup

	for i := 1; i <= 5; i++ {
   
		wg.Add(1)
		go worker(i, &wg)
	}

	wg.Wait()
}

这里首先把wg 计数设置为1, 每个for循环运行完毕都把计数器减一,主函数中使用Wait() 一直阻塞,直到wg为1——也就是所有的5个for循环都运行完毕。

使用注意点:

  • 计数器不能为负值
  • WaitGroup对象不是引用类型

2.Once

sync.Once可以控制函数只能被调用一次,不能多次重复调用。

例如:

var doOnce sync.Once

func main() {
   
	DoSomething()
	DoSomething()
}

func DoSomething() {
   
	doOnce.Do(func() {
   
		fmt.Println("Run once - first time, loading...")
	})
	fmt.Println("Run this every time")
}

输出:

Run once - first time, loading...
Run this every time
Run this every tim

3.互斥锁Mutex

互斥锁是并发程序对共享资源进行访问控制的主要手段,在go中的sync中提供了Mutex的支持。

例如:使用互斥锁解决多个Goroutine访问同一变量。

// SafeCounter 的并发使用是安全的。
type SafeCounter struct {
   
	v   map[string]int
	mux sync.Mutex
}

// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
   
  c.mux.Lock()
  defer c.mux.Unlock()
	// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
  c.v[key]++
}

// Value 返回给定 key 的计数器的当前值。
func (c *SafeCounter) Value(key string) int {
   
	c.mux.Lock()
	// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
	defer c.mux.Unlock()
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值