go笔记-3

并发和并行

并发是只一次做多件事情,但是同一时间只做一件事情
并行是一次做多件事情,并且这些事情在同一时间内都在处理

在函数前加入go关键字来新建一个并发的Goroutines
go funcName(1)
任何Gorountines都依附在main goroutines上,因此主进程结束时所有Goroutines都会立即结束,即使没有处理完毕

channels

类型前加入chan关键字来声明,如
var a chan int
a := make(chan int)
前者a == nil,后者则定义了一个名为a的channel

channel的数据读写

1
2
data := <- a//从a中读取数据,直到读取到数据才往下运行
a <- data //向a中写入数据

用法举例

1
2
3
4
5
6
7
8
9
10
func hello(done chan bool) {  
fmt.Println("Hello world goroutine")
done <- true
}
func main() {
done := make(chan bool)
go hello(done)
<-done
fmt.Println("main function")
}

若channels写入数据但是该通道没有地方读取该数据,将会造成deadlock错误
参数传入时,可以使用chan<- int,把传入的双向通道改为只写通道,函数内只能向通道写入数据,不能读取数据

通道中的每一条消息只能被收到一次,即使有多个函数试图接收,也只会有一个收到
可以使用’close(ch)’关闭ch通道,此时所有没有收到的都会收到关闭的数据
·v,ok<-ch的ok来判断ch通道是否是因为close而关闭,若关闭,收到的值为对应数据的零值for v:=range ch`可以一直循环直到ch关闭

buffered channels

发送时若buffer满了则会阻塞,受到时若buffer为空则阻塞
声明方法
ch := make(chan type, capacity)
capacity为buffer的容量,意味着发送时未被收接收的数据存放数量一旦高于就会阻塞
可以通过len(eh)获取还没有处理的数据,cap(ch)获取容量

waitgroup

声明方法var wg sync.WaitGroup
wg.Add(1)添加计数,wg.Down()减少计数
wg.Wait()等待直到计数归零
waitgroup作为实参传递的时候,需要加上&防止函数内部传到的是副本而不是本身

workpool

处理的任务的方式

(并发1个)输入函数alloacate,将输入传递到’输入用channels’中
(并发n个)处理函数worker,从‘输入用channels’获取数据,处理后放在’输出用channels’
(并发1个)输出函数result,从’输出用channels’中获取数据用于输出

其中’输入用channels’和’输出用channels’为buffered pool,可以储存一定量的数据供处理
n个worker函数使得任务可以一次并发处理多个

个人认为好处可能是是当其中1个任务工作量太大的时候,依旧可以处理其它工作量较小的任务,而不至于被大任务完全阻塞?

select

1
2
3
4
5
6
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}

output1,output2是channel
类似switch,但是是用来处理channel的,当有channel收到数据的时候,就会执行该分支,在这之前将会阻塞
若同时收到数据的有多个,则随机选择一个分支执行

分支可以使用default,代表没有任何一个case的channel收到了数据则执行
个人认为存在default分支的select不会造成阻塞,因为要么收到了数据随机执行分支,要么没有收到执行default,不会有等待的时刻

select中default的使用可以避免死锁
空的select会造成永久阻塞

Mutex

临界区(Critical section)

每个线程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。
摘自百度百科

1
2
3
mutex.Lock()  
x = x + 1
mutex.Unlock()

mutex在Lock时会检测是否已经上锁了,如果有,则阻塞直到原先已经上锁的那边的锁的解除(即Unlock被执行)
可以防止因为多个Goroutines并发,同时修改同一个数据造成的数据错乱
和WaitGroup一样,传递Mutex变量需要使用&来确保不同函数传入的是同一个而非拷贝,因为Mutex只有在同一个变量上锁的情况下才会阻塞

也可以使用channel达到与mutex相同的效果,令ch为capacity为1的buffered channel

1
2
3
ch<-true//存在数据,其它函数内的ch只能阻塞直到这个数据被接收
x = x+1
<-ch//接收ch内的数据,其它函数内的ch现在可以解除阻塞来传入数据了

“仿造”Class

因为go里并没有提供’构造函数’功能,我们只能声明一个函数,假装它是构造函数:

将struct用小写(比如说 a)设置为外部不可见,这样外部就不能直接声明变量来获得这个类型
构造一个函数(比如用Nwe命名),生成一个a类型,并且为其赋值,然后返回这个a类型
由于外部只能通过这个函数生成到这种类型的数据,这个函数将充当构造函数

Defer

在函数或者方法前面添加defer关键词,可以让它在return之前才调用,但是保留当时参数的值,如

1
2
3
4
5
6
7
8
9
10
func printA(a int){
fmt.Println("PrintA:",a)
}

func print(){
a:=5
defer printA(a)
a=10
fmt.Println("Print:",a)
}

输出为

1
2
Print: 10
PrintA: 5

多个defer采用栈(LIFO)后进先出的顺序执行

可用于有多个return分支但是又希望在return前进行统一动作的时候使用

Error

自带有error接口,只要声明了Error()函数的类型,就可以作为错误输出数据
此时Println()函数会调用该数据的Error,输出其返回的string
errors.New("错误信息")也可以用来创建新的错误数据
fmt.Errorf("错误信息,附带值:%d",10)同样可以用来创建新的错误数据

通过类型断言来转化错误类型获得更多信息
比如说对于打开文件f,err:=os.Opem("path/"),可以断言err
if err, ok := err.(*os.PathError);ok这样就可以用err.path获取到路径信息

func panic(interface{})可以用来输出那些可能会导致程序无法继续的错误
调用该函数后,调用者函数将会被中止,未处理的deder的函数将会被处理
然后以同样的方式中止(调用(调用(panic函数)的函数)的函数),逐级向外推进,直到推进到没有外层函数为止(即程序中止)
程序中止时,将输出调用panic函数的位置追踪。

可以在deder中调用func recover() interface{}来中止panic向外推进的过程
例如在

1
2
3
4
5
func recoverName() {  
if r := recover(); r!= nil {
fmt.Println("recovered from ", r)
}
}

那么在调用defer recoverName()的函数不再向外层传递panic的影响(但调用函数本身依旧受到影响)
这使得panic只限制于一个范围内,而非让整个程序终止
recover只限于恢复同一个goroutine内的函数,无法在一个goroutine下恢复另一个goroutine造成的panic

runtime error也是内建的panic,所以也可以通过recover恢复

recover会中止错误路径追踪,如果需要追踪,则要在recover的函数内添加debug.PrintStack()函数来打印它

一级函数

(关于类型的分类资料)[http://rednaxelafx.iteye.com/blog/184199]
类型:规定了变量可以取的值得范围,以及该类型的值可以进行的操作。根据类型的值的可赋值状况,可以把类型分为三类:

1、一级的(first class)。该等级类型的值可以传给子程序作为参数,可以从子程序里返回,可以赋给变量。大多数程序设计语言里,整型、字符类型等简单类型都是一级的。
2、二级的(second class)。该等级类型的值可以传给子程序作为参数,但是不能从子程序里返回,也不能赋给变量。
3、三级的(third class)。该等级类型的值连作为参数传递也不行。

所以一级函数就是能作为参数传递的函数

允许闭包,允许匿名函数,允许函数作为参数或者返回值

文章目录
  1. 1. 并发和并行
  2. 2. channels
  3. 3. buffered channels
  4. 4. waitgroup
  5. 5. workpool
  6. 6. select
  7. 7. Mutex
  8. 8. “仿造”Class
  9. 9. Defer
  10. 10. Error
  11. 11. 一级函数
|