前言
以前听说什么golang一把梭什么的很厉害,到现在位置也接触golang半个多月时间了,最主要的时间都是在看document,学习语法。golang什么的变量名大小写区分公有私有神马的太坑爹了。。。最近接触到了golang最为称赞的第一个地方:goroutine。本篇博客先介绍操作系统中的一些基本语法,然后介绍一下goroutine的美妙之处。
【线程, 进程】; 【多进程,多线程】; 【并发,并行】
线程与进程
- 概念
- 线程:是程序执行流的最小单元,是系统独立调度和分配CPU(独立运行)的基本单位。
- 进程:是资源分配的基本单位。一个进程包括多个线程
- 区别
- 线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。
- 每个进程都有自己一套独立的资源(数据),供其内的所有线程共享。
- 不论是大小,开销线程要更“轻量级”
- 一个进程内的线程通信比进程之间的通信更快速,有效。(因为共享变量)
多进程与多线程
多线程:同一时刻执行多个线程。用浏览器一边下载,一边听歌,一边看视频,一边看网页。。。
多进程:同时执行多个程序。如,同事运行YY,QQ,以及各种浏览器。
并发与并行
并发当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
举个例子:
并发就是一个窗口多条队伍,多条队伍轮流使用资源。
并行就是多个窗口,每个窗口一个队伍,队伍之间并行排队。
gotuntine
golang语言中,用go关键词就可以启动并发程序。
我们先看一下下面这个简单的例子,在main函数中依次调用了两次loop函数。
func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d", i)
}
}
func main() {
loop()
loop()
}
毫无疑问上述代码的运行结果如下:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
这个时候我们将main函数代码换成如下
func main() {
go loop()
loop()
}
此时loop函数只运行了一次;这是什么原因呢,原来是因为我们的main函数中的第二个loop()执行比较快
在go loop()还没有执行的时候,main函数已经运行结束并且退出了。
0 1 2 3 4 5 6 7 8 9
我们可以使用time.Sleep设置延时来等待go loop()执行,这种做法可行,但是显然是不科学的,如果我的go loop()执行很快结束了,而main函数还在等待,这显然是一种资源上的浪费。
那么如何解决的,我们可以用下面的信道来解决这个问题
func main() {
go loop()
loop()
time.Sleep(time.Seoncd) //停顿一秒
}
信道
信道是什么?简单的来说就是goruntine之间相互通讯的东西。类似Linux中的管道,用来在多个进程之间共享资源。
先看一下例子
func main() {
var chanel chan int = make(chan int) // 定义信道类型变量 chanel
go func(msg string) {
chanel <- msg // 存消息 chan <- msg
}("Ping")
msgStr := <-chanel // 取消息 msg <- chan
fmt.Println(msgStr)
}
在上述代码中,我们通过定义chanel变量完成了进程间的消息通讯。
通过存消息和取消息两种方法完成进程间的消息通讯。
通过这种方式我们可以控制进程的结束时间
var quit chan int = make(chan int)
func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
quit <- 0 // 进程执行结束,存入消息
}
func main() {
go loop()
loop()
<- quit // 取消息,进程结束
}
我们通过quit来控制loop()的结束时间点,当quit没有取到消息的时候,进程就会阻塞,main函数也就无法继续执行。
如果不用信道来阻塞主线的话,主线就会过早跑完,loop线都没有机会执行、、、
其实,无缓冲的信道永远不会存储数据,只负责数据的流通,为什么这么讲呢?
- 从无缓冲信道取数据,必须要有数据流进来才可以,否则当前线阻塞
- 数据流入无缓冲信道, 如果没有其他goroutine来拿走这个数据,那么当前线阻塞
简单来说就是必须有消息流入,并且有消息流出,缺一不可,不然会造成死锁
后续
下一篇博客将会介绍一下死锁以及等待多goruntine的方案