一.并发编程
多进程:是操作系统层面进行并发的基本模式,同时也是开销最大的模式,通过IP+端口来唯一标识一个进程。在操作系统层面,进程其实就是一个0-3G内存块。
多线程:多线程在大部分操作系统上都属于系统层面的并发模式,也是现在我们使用比较多一种有效模式。比进程开销小,其实比起协程的话,器开销还是比较大的,并且在一个高并发模式,效率会比较低
基于回调的非阻塞的/异步IO:这种架构的诞生实际上来源于多线程模式危机,在很多的高并发服务器开发实践中,使用这种多线程的模式会很快耗尽服务器内存和CPU资源。而这种模式通过事件驱动的方式使用异步IO,可以使服务持续运转,尽可能少地使用线程,降低了开销。NodeJS就是最好的实战。
协程:本质上是一种用户态的线程,不需要操作系统的抢占式调度,并且在真正的实现寄存器线程中。因此,开销特别小。
二.协程
go语言在语言级别支持轻量级线程,叫goroutine。go语言标准库提供的所有系统调用操作(包含所有同步IO操作),都会让出CPU给其他goroutine,这样的话,让轻量级的线程的切换管理就不会依赖于系统的进程和线程,也不依赖于CPU的核心数量。
三.goroutine
goroutine是go语言中轻量级线程的实现,由go运行时(runtime)管理。当然,协程在go特别简单,不想进程和线程操作那么复杂。
案例:用协程做一个小案例
packagemain
import"fmt"
funcAdd(x, y int) {
z := x + y
fmt.Println("有名函数", z)
}
funcmain() {
fmt.Println("hello world")
goAdd(1,5)
go func(x, y int) { fmt.Println("匿名函数", x + y)}(1,2)
}
go是定义协程关键字,在一个函数的前加关键字,本次调用就会在一个新的goroutine中运行。当被调用的函数返回时,这个goroutine也就自动结束了。需要注意的是,如果这个函数有返回值的话,那么返回值会被丢弃了。
package main
import "fmt"
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
func main() {
fmt.Println("hello world")
for i := 0; i
go Add(2, 6)
}
}
实际上,上面的代码可能不会有任何Add方法求出的结果的输出,原因是main函数退出时,可能还没有执行go协程,当main函数退出的时候,程序退出,并且程序不会等待其他的goroutine的执行(非主goroutine)结束。
问题简单解决代码如下:
package main
import (
"fmt"
"sync"
)
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
func main() {
var wg sync.WaitGroup //声明一个WaitGroup变量
wg.Add(1)
fmt.Println("hello world")
for i := 0; i
go Add(2, 6)
}
wg.Wait();
}
1.并发通信
并发通信就是多个协程同时去操作同一个共享数据,共享数据是指多个并发单元分别保持对同一个数据的引用,实现对该数据的共享。被共享的数据可能有多种形式,比如内存数据,磁盘文件,网络数据等。实际工作中用得做多是内存数据。
下面的案例是通过全局变量来做一个并发通信:
package main
import (
"fmt"
"sync"
"runtime"
)
var counter int = 0
func Count(lock *sync.Mutex) {
lock.Lock()
counter++
fmt.Println(counter)
lock.Unlock()
}
func main() {
lock := &sync.Mutex{}
for i := 0; i
go Count(lock)
}
for {
lock.Lock()
c := counter
lock.Unlock()
runtime.Gosched()
if c >= 10 {
break
}
}
}
在上面的例子中,我们用了10个协程共享了counter,每个协程执行完成之后,counter会去自动+1,由于10个协程是并发执行,故而咱们引入了互斥锁。每次咱们队count做自加操作都要锁定和解锁,在main函数用for循环来检查counter的值,同样加锁解锁,这样的代码很繁琐。不符合咱们go语言的优雅编程。
针对上面这种比较繁琐的代码机制,咱们go语言提供了另种一种通信,即以消息机制而非共享内存做为通信。
消息机制认为每个并发单元是自包含,独立的个体,并且都有自己的变量,但是不同并发单元之间这些变量是不共享。每个并发单元的输入和输出只有一种,那就是消息。
go语言的这种消息机制被称为channel。
2.channel
channel是go语言在语言级别提供的goroutine间的通信,我们可以使用channel在两个或者多个协程之间来传递消息。channel是进程内的通信方式,因此通过channel传递对象的过程和调用函数时的参数传递行为比较一致,比如指针等。
channel是类型相关的,也就是说,一个channel只能传递一种类型的值,这个类型需要在声明channel时指定。可以将其认为是一种类型安全的管道。
channel案例:
package main
import "fmt"
func Count(ch chan int) {
fmt.Println("Counting")
}
func main(){
chs := make([]chan int , 10)
for i := 0; i
chs[i] = make(chan int)
go Count( chs[i])
}
for _, ch := range(chs) {
}
}
领取专属 10元无门槛券
私享最新 技术干货