Go语言并发之道学习三 select

select for-select控制channel

package main

import (
	"fmt"
	"time"
)

/*
select语句是将channel绑定在一起的黏合剂,这就是我们如何在一个程序中组合channel以形式更大的抽象事物的方式。
*/

/*
声明select语句是一个具有并发性的Go语言程序中最重要的事情之一,这并不是夸大其词。
在一起系统中两个或多个组件的交集中,可以在本地、单个函数或类型以及全局范围内找到select语言绑定在一起的channel。
除了连接组件之外,在程序中的这些关键节点上,select语句可以帮助安全的将channel与诸如取消、超时、等待和默认值之类的概念结合在一起。
*/

func main() {
	fmt.Println("testSelect")
	testSelect()
}

func testSelect(){
	var c1 <-chan interface{}//读
	c1chan := func() <-chan interface{}{
		cmodule := make(chan interface{},1)
		cmodule<-"c1"
		return  cmodule
	}
	c1 = c1chan()
	select {
	case <-c1:
		fmt.Println("<-c1")
	}
	fmt.Println()

	//---------------------
	var c2 chan<- interface{} //写
	c2 = make(chan<- interface{},1)
	select {
	case c2<-struct {}{}:
		fmt.Println("c2<-struct {}{}")
	}
	close(c2)
	fmt.Println()

	//---------------------
	// 双向的 channel 在读取的情况下close后,变成可用的channel
	// 双向的 channel 在写入不具备下游消费能力的channel ,变成可用的channel
	// 可用的channel 才能被 select执行
	start := time.Now()
	c3 := make(chan interface{})
	go func() {
		time.Sleep(1*time.Second)
		close(c3)
	}()

	fmt.Println("blocking on read...")
	//读取的情况下close后,变成可用的channel
	select {
	case <-c3:
		fmt.Printf("unblocking %v later\n",time.Since(start))
	}
	//写入不具备下游消费能力的channel ,变成可用的channel
	c4 := make(chan interface{},1)
	select {
	case c4<-struct {}{}:
		fmt.Println("c4<-struct {}{}")
	}
	close(c4)
	fmt.Println()

	//---------------------
	//同时有两个可用的channel
	//select 将随机运行其中一个 case
	case1 := make(chan interface{});close(case1)
	case2 := make(chan interface{});close(case2)

	var c1Count,c2Count int
	for i := 0;i <= 100 ;i++  {
		select {
		case <-case1:
			c1Count++
		case <-case2:
			c2Count++
		}
	}
	fmt.Printf("c1Count: %d\nc2Count: %d\n",c1Count,c2Count)
	fmt.Println()

	//----------------------
	//select 超时
	var c5 <-chan int
	select {
	case <-c5: //这个case阻塞了,因为 c5是一个 nil channel
	case <-time.After(1*time.Second):
		fmt.Println("time out")
	}
	fmt.Println()

	//-----------------------
	//select default默认语句
	//当所有channel都被阻塞时,default也会被调用
	start1 := time.Now()
	var c6,c7 <-chan int
	select {
	case <-c6: //阻塞
	case <-c7://阻塞
	default:
		fmt.Printf("In default after %v\n",time.Since(start1))
	}
	fmt.Println()

	//------------------------
	//for-select循环
	done := make(chan interface{})
	go func() {
		time.Sleep(1*time.Second)
		close(done)
	}()
	workCounter := 0
	loop:
		for {
			select {
			case <-done: //case阻塞,不会被触发,直到close(done)
				break loop  //close(done)后,打断loop循环
			default:
			}
			//模拟工作行为
			workCounter++
			time.Sleep(200*time.Millisecond)
		}
	fmt.Printf("Achived %v cycles of work before signalled to stop\n",workCounter)
	fmt.Println()

	//-------------------------------
	//runtime.GOMAXPROCS(runtime.NumCPU())
	/*
	runtime.GOMAXPROCS(CPU的数量) 可以设置多核执行任务
	通常情况下它自动设置为主机上逻辑CPU的数量 runtime.NumCPU()

	特殊情况下,我们会去调整它:
	有一个项目,被竞争环境困扰。有时候测试失败,我们运行测试的主机有4个逻辑CPU
	因此在任何一个点上,我们都有4个goroutine同时执行
	通过增加GOMAXPROCS 已超过我们拥有的逻辑CPU数量,我们能够更频繁的触发竞争条件,从而更快的修复他们

	其他人可能通过实验发现,他们的程序在一定数量的工作队列和线程上运行的更换,但我更主张谨慎些。
	如果你通过调整这个方法来压缩性能,那么在每次提交之后,当你使用不同的硬件,以及使用不同版本的Go语言时,一定要这样做。
	调整这个值会使你的程序更接近它所运行的硬件,但以抽象和长期性能稳定为代价
	*/


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书作者带你一步一步深入这些方法。你将理解 Go语言为何选定这些并发模型,这些模型又会带来什么问题,以及你如何组合利用这些模型中的原语去解决问题。学习那些让你在独立且自信的编写与实现任何规模并发系统时所需要用到的技巧和工具。 理解Go语言如何解决并发难以编写正确这一根本问题。 学习并发与并行的关键性区别。 深入到Go语言的内存同步原语。 利用这些模式中的原语编写可维护的并发代码。 将模式组合成为一系列的实践,使你能够编写大规模的分布式系统。 学习 goroutine 背后的复杂性,以及Go语言的运行时如何将所有东西连接在一起。 作者简介 · · · · · · Katherine Cox-Buday是一名计算机科学家,目前工作于 Simple online banking。她的业余爱好包括软件工程、创作、Go 语言(igo、baduk、weiquei) 以及音乐,这些都是她长期的追求,并且有着不同层面的贡献。 目录 · · · · · · 前言 1 第1章 并发概述 9 摩尔定律,Web Scale和我们所陷入的混乱 10 为什么并发很难? 12 竞争条件 13 原子性 15 内存访问同步 17 死锁、活锁和饥饿 20 确定并发安全 28 面对复杂性的简单性 31 第2章 对你的代码建模:通信顺序进程 33 并发与并行的区别 33 什么是CSP 37 如何帮助你 40 Go语言并发哲学 43 第3章 Go语言并发组件 47 goroutine 47 sync包 58 WaitGroup 58 互斥锁和读写锁 60 cond 64 once 69 池 71 channel 76 select 语句 92 GOMAXPROCS控制 97 小结 98 第4章 Go语言并发模式 99 约束 99 for-select循环103 防止goroutine泄漏 104 or-channel 109 错误处理112 pipeline 116 构建pipeline的最佳实践 120 一些便利的生成器 126 扇入,扇出 132 or-done-channel 137 tee-channel 139 桥接channel模式 140 队列排队143 context包 151 小结 168 第5章 大规模并发 169 异常传递169 超时和取消 178 心跳 184 复制请求197 速率限制199 治愈异常的goroutine 215 小结 222 第6章 goroutine和Go语言运行时 223 工作窃取223 窃取任务还是续体 231 向开发人员展示所有这些信息 240 尾声 240 附录A 241
### 回答1: 学习Go语言并发编程,需要掌握以下几个方面: 1. 学习Goroutine:Goroutine是Go语言并发编程的核心,它是一种轻量级的线程,可以在程序中创建多个Goroutine并发执行,以实现高效的并发编程。需要学习如何创建和管理Goroutine。 2. 学习Channel:Channel是Goroutine之间进行通信的重要手段,可以用于数据传输和同步等操作。需要学习如何创建和使用Channel。 3. 学习Select语句:Select语句是Goroutine之间进行多路复用的重要语法,可以同时监听多个Channel,从而实现高效的并发处理。需要学习如何使用Select语句。 4. 学习Mutex和WaitGroup:Mutex和WaitGroup是Go语言中实现同步和互斥的重要机制,可以用于保护共享资源和协调Goroutine的执行。需要学习如何使用Mutex和WaitGroup。 5. 学习并发编程的设计模式:并发编程的设计模式是一些常用的并发编程思想和模式,可以用于解决并发编程中的常见问题。需要学习如何应用并发编程的设计模式。 为了学习并发编程,可以参考一些优秀的Go语言并发编程书籍,比如《Go并发编程实战》、《Go语言并发与微服务实战》等。同时也可以参考一些优秀的开源项目,如etcd、Docker等,深入了解Go语言并发编程的应用场景和实现方式。 ### 回答2: 学习Go语言并发编程可以按照以下步骤进行。 1. 学习并理解Go语言并发模型:Go语言并发编程基于goroutine和channel。首先,需要了解goroutine的概念,它是Go语言中的一种轻量级线程。然后学习如何使用channel进行通信和同步。 2. 掌握goroutine的创建和管理:学习如何创建和管理goroutine,可以通过使用go关键字来创建一个新的goroutine,以便并发地执行任务。 3. 理解channel的使用:掌握channel的使用,了解如何创建、发送和接收数据。学习不同类型的channel以及它们在并发编程中的应用场景。 4. 学习互斥锁和读写锁:Go语言提供了互斥锁和读写锁来实现资源的安全访问。深入理解锁的概念和使用方法,学习如何避免并发访问导致的数据竞争。 5. 掌握并发编程的常见模式:学习并发编程中的常见模式,例如生产者-消费者模式、多路复用模式、线程池等。熟悉这些模式可以帮助我们更好地设计并发程序。 6. 阅读优秀的并发编程代码和文档:阅读优秀的并发编程代码可以提供实际的应用示例和启发。同时,阅读官方文档和相关书籍也是学习的重要途径。 7. 实践和调试:编写自己的并发代码,利用调试工具对程序进行调试,观察并发执行的过程和结果。通过实践来提高对并发编程的理解和应用能力。 总之,学习Go语言并发编程需要理解并发模型、掌握goroutine和channel的使用、了解锁的概念和使用方法,并通过实践来提高自己的技能。 ### 回答3: 学习Go语言并发编程可以通过以下几个步骤来进行。 首先,了解并发编程的概念和原则。并发编程是指同时进行多个任务,使用多个线程或协程来提高程序的效率和性能。了解并发编程的基本概念,如协程、锁、原子操作等,对学习Go语言并发编程非常重要。 其次,学习Go语言并发特性。Go语言内置了丰富的并发编程工具和特性,如goroutine、channel、select等。通过学习这些特性,可以更好地理解Go语言中的并发编程模型,并能够正确地使用它们。 然后,阅读相关的书籍和文章。有很多经典的书籍和文章涉及到Go语言并发编程,这些资源可以帮助你更深入地理解并发编程的原理和实践。值得推荐的书籍有《Go语言实战》和《Go并发编程实战》。 接着,进行实践和项目练习。通过编写一些小型的并发程序和项目,可以将理论知识应用到实践中,加深对并发编程的理解和掌握。可以尝试使用goroutine和channel来实现一些并发任务,如爬虫、并发请求等。 最后,参与社区和交流。加入Go语言的社区,如论坛、聊天群等,与其他开发者交流和分享经验。通过与他人的交流,可以学习到更多实践中的经验和技巧,不断提升自己的并发编程能力。 总之,学习Go语言并发编程需要系统地学习基本概念和原则,掌握语言特性,进行实践和项目练习,并积极参与社区和交流。只有不断实践和学习,才能在并发编程领域不断进步。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值