Go程序设计3——并发编程
1 channel
一般channel的声明形式为:
var chanName chan ElementType
  与一般的变量声明不同的地方仅仅是在类型之前增加了chan关键字。ElementType指定这个channel所能传递的元素类型。举个例子,我们声明一个传递类型的int的channel:
var ch chan int
  或者声明一个map,元素是bool型的channel
var m map[String] chan bool
  定义一个channel也很简单,直接使用内置的函数make()即可:
ch := make(chan int)
  这就声明并初始化了一个int型的名为ch的channel。在channel的用法中,最常见的包括写入和读出,将一个数据写入至channel的语法很直观。
ch <- value
  向channel写入数据通常会导致程序阻塞,直到有其他goroutine从这个channel中读取数据。从channel中读取数据的语法是:
value := <- ch
  如果channel之前没有写入数据,那么从channel中读取数据也会导致程序阻塞,直到channel中被写入数据为止。我们之后还会提到如何控制channel只接受写或者只允许读取,即单向channel。
1.1 select
  早在Unix时代,select机制就已经被引入,通过调用select函数来监控一系列的文件句柄,一旦其中一个文件句柄发生了IO动作,该select调用就会被返回。后来该机制也被用于实现高并发的Socket服务器程序。Go语言直接在语言级别支持select关键字,用于处理异步IO问题。
  select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述,与switch语句可以选择任何可使用相等比较的条件相比,select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作,大致结构如下:
select {
 case <-chan1:
 // 如果chan1成功读到数据,则进行该case处理语句
 case chan2 <- 1:
 // 如果成功向chan2写入数据,则进行该case处理语句
  default:
 // 如果上面都没有成功,则进入default处理流程
} 
可以看出,select不像switch,后面并不带判断条件,而是直接去查看case语句。每个case语句都必须是一个面向channel的操作。比如上面的例子中,第一个case试图从chan1读取
一个数据并直接忽略读到的数据,而第二个case则是试图向chan2中写入一个整型数1,如果这两者都没有成功,则到达default语句。
ch := make(chan int, 1)
for{
select{
case ch <- 0:
case ch <- 1:
}
i := <- ch
fmt.Println("Value received:", i)
}
1.2 缓冲机制
  之前我们示范创建的都是不带缓冲的channel,这种做法对于传递单个数据的场景可以接受,但对于需要持续传输大量数据的场景就有些不合适了。接下来我们介绍如何给channel带上缓冲,从而达到消息队列的效果。 要创建一个带缓冲的channel,其实也非常容易: 
c := make(chan int, 1024) 
  在调用make()时将缓冲区大小作为第二个参数传入即可,比如上面这个例子就创建了一个大小为1024的int类型channel,即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞从带缓冲的channel中读取数据可以使用与常规非缓冲channel完全一致的方法,但我们也可以使用range关键来实现更为简便的循环读取:
for i := range c {
    fmt.Println("Received:", i)
} 
1.3 channel的传递
  需要注意的是,在Go语言中channel本身也是一个原生类型,与map之类的类型地位一样,因此channel本身在定义后也可以通过channel来传递。channel和Unix的管道pipe很类似。下面我们利用channel可被传递的特性来实现我们的管道,为了简化表达,假设在管道中传递的数据只是一个整型数,在实际场景中通常是一个数据块。
首先限定基本的数据结构:
type PipeData struct{
    value int
    handler func(int) int
    next chan int
}
1.4 单向channel
  单向channel只能用于发送或者接受数据,channel本身必然是同时支持读写的,否则根本没法用。如果只允许读,那就没法写数据,所以channel是空的,如果只允许写,没法读,那没有意义,因为没法读。
  单向channel变量的声明非常简单,如下:
var ch1 chan int // ch1是一个正常的channel,不是单向的 
var ch2 chan<- float64// ch2是单向channel,只用于写float64数据 
var ch3 <-chan int  // ch3是单向channel,只用于读取int数据 
关闭channel
  关闭channel很简单,使用close函数
close(ch)
2 多核并行化
  在执行一些昂贵的计算任务时,我们希望能够尽量利用现代服务器普遍具备的多核特性来尽量将任务并行化,从而达到降低总计算时间的目的。此时我们需要了解CPU核心的数量,并针对
性地分解计算任务到多个goroutine中去并行运行。
下面来模拟一个完全可以并行的计算任务,计算N个整型数的总和,我们可以将所有整型数分成M份,M即CPU的个数,让每个CPU开始计算分给它的那份计算任务,最后将每个CPU的计算结果做累加,得到所有N个整型数的总和:
type Vector[] float64
//分配给每个CPU的计算任务
func (v Vector) DoSome(i, n int, u Vector, c chan int){
for ; i < n; i++{
v[i] += u.Op(v[i])
}
c <-
} const NCPU = // 假设总共有16核 func (v Vector) DoAll(u Vector) { c := make(chan int, NCPU) // 用于接收每个CPU的任务完成信号 for i := ; i < NCPU; i++ {
go v.DoSome(i*len(v)/NCPU, (i+)*len(v)/NCPU, u, c)
} // 等待所有CPU的任务完成
for i := ; i < NCPU; i++ {
<-c // 获取到一个数据,表示一个CPU计算完成了
}
// 到这里表示所有计算已经结束
}
  这两个函数看起来设计非常合理。DoAll()会根据CPU核心的数目对任务进行分割,然后开辟多个goroutine来并行执行这些计算任务。执行后,计算时间并没有降到原来的1/N,答案是否定的,因为当前版本还没有支持。还是单核的。官方的答案是,这是当前版本的Go编译器还不能很智能地去发现和利用多核的优势。虽然我们确实创建了多个goroutine,并且从运行状态看这些goroutine也都在并行运行,但实际上所有这些goroutine都运行在同一个CPU核心上,在一个goroutine得到时间片执行的时候,其他goroutine都会处于等待状态。从这一点可以看出,虽然goroutine简化了我们写并行代码的过程,但实际上整体运行效率并不真正高于单线程程序。 
在Go语言升级到默认支持多CPU的某个版本之前,我们可以先通过设置环境变量
  GOMAXPROCS的值来控制使用多少个CPU核心。具体操作方法是通过直接设置环境变量GOMAXPROCS的值,或者在代码中启动goroutine之前先调用以下这个语句以设置使用16个CPU核心: 
runtime.GOMAXPROCS(16) 
  到底应该设置多少个CPU核心呢,其实runtime包中还提供了另外一个函数NumCPU()来获取核心数。可以看到,Go语言其实已经感知到所有的环境信息,下一版本中完全可以利用这些
信息将goroutine调度到所有CPU核心上,从而最大化地利用服务器的多核计算能力。抛弃GOMAXPROCS只是个时间问题。
Go程序设计3——并发编程的更多相关文章
- 已看1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDBC、XML、反射等。[泛型]\
		
1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架.多线程(并发编程).I/O(NIO).Socket.JDBC.XML.反射等.[泛型]\1* ...
 - Java并发编程:并发容器之CopyOnWriteArrayList(转载)
		
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
 - Erlang 102 Erlang并发编程
		
笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期 变更说明 2014-11-02 A outline 2014 ...
 - Java并发编程:并发容器之CopyOnWriteArrayList
		
转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...
 - 《C++ 并发编程》- 第1章 你好,C++的并发世界
		
<C++ 并发编程>- 第1章 你好,C++的并发世界 转载自并发编程网 – ifeve.com 本文是<C++ 并发编程>的第一章,感谢人民邮电出版社授权并发编程网发表此文, ...
 - Java并发编程:CopyOnWrite容器的实现
		
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
 - java并发编程的艺术——第四章总结
		
第四章并发编程基础 4.1线程简介 4.2启动与终止线程 4.3线程间通信 4.4线程应用实例 java语言是内置对多线程支持的. 为什么使用多线程: 首先线程是操作系统最小的调度单元,多核心.多个线 ...
 - python并发编程之多进程(三):共享数据&进程池
		
一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...
 - Java 面试知识点解析(二)——高并发编程篇
		
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
 
随机推荐
- LeetCode IPO
			
原题链接在这里:https://leetcode.com/problems/ipo/description/ 题目: Suppose LeetCode will start its IPO soon. ...
 - C语言 产生随机数
			
rand()函数 #include <stdlib.h> int rand(void); rand()是根据某个种子,以特定的算法,计算出一系列数的函数.返回的数在0和RAND_MAX之间 ...
 - C#网络编程(异步传输字符串) - Part.3
			
这篇文章我们将前进一大步,使用异步的方式来对服务端编程,以使它成为一个真正意义上的服务器:可以为多个客户端的多次请求服务.但是开始之前,我们需要解决上一节中遗留的一个问题. 消息发送时的问题 这个问题 ...
 - 解决ubantu中sublime不支持中文的方法
			
更新然后将系统升级到最新版本,在linux终端输入 sudo apt-get update && sudo apt-get 在本地目录中克隆此repo: 如果你没有git的话就安 ...
 - 笔记:C 编译过程
			
笔记:C 编译过程 参考了 编译器的工作过程 1 C 编译过程 配置 确定标准库和头文件位置 确定依赖关系 头文件的预编译 预处理 编译 连接 F4NNIU 2018-06-12 编译器的工作过程 h ...
 - 如何在B2C电子商务网站后台添加CNZZ统计代码(转)
			
CNZZ作为网站流量数据统计分析工具的一种,和百度统计工具类似,同样也是用于查看分析网站所有流量数据来源的一种站长工具,当然商家可根据自己的习惯在B2C电子商务网站后台添加相应的数据统计代码来管理您的 ...
 - Oracle存储过程使用总结
			
1.使用Oracle存储过程查询结果集: 网上写的都是他妈的扯淡!其实一句话就行了,你只要返回一个游标就OK了.具体代码如下: CREATE OR REPLACE PROCEDURE PR_ORDER ...
 - fragment用法
			
简单用法: 1.新建布局.新建fragment类 2.在activity_main.xml中添加fragment <LinearLayout...... <fragment android ...
 - 数据科学:Pandas 和 Series 的 describe() 方法
			
一.Pandas 和 Series 的 describe() 方法 1)功能 功能:对数据中每一列数进行统计分析:(以“列”为单位进行统计分析) 默认只先对“number”的列进行统计分析: 一列数据 ...
 - php.ini修改php上传文件大小限制
			
打开php.ini,首先找到file_uploads = on ;是否允许通过HTTP上传文件的开关.默认为ON即是开upload_tmp_dir ;文件上传至服务器上存储临时文件的地方,如果没指定就 ...