并发和并行的区别
- 并发是指一个处理器同时处理多个任务。
- 并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发意思就是一个时间段一个处理器处理任务
并行是多个处理器处理任务
线程与协程的区别
-
一个线程可以多个协程,一个进程也可以单独拥有多个协程。
-
线程进程都是同步机制,而协程则是异步。
-
线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
-
协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器可以关联任意线程或线程池, 可以使当前线程, UI线程, 或新建新程
Goroutine
只需在函数调⽤语句前添加 go 关键字,就可创建并发执⾏单元。
简单实例
package concurrence
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
复制代码
改进实例
sync.WaitGroup也是一个经常会用到的同步原语,它的使用场景是在一个goroutine等待一组goroutine执行完成。
package main
import (
"fmt"
"sync"
)
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGoRoutine(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
hello(i)
}
}
func main() {
wg :=sync.WaitGroup{}
wg.Add(1)
HelloGoRoutine(&wg)
wg.Wait()
fmt.Println("i我在最后")
}
复制代码
Channel
如果说goroutine是Go并发的执行体,那么"通道"就是他们之间的连接。
通道可以让一个goroutine发送特定的值到另外一个goroutine的通信机制 简而言之Channel可以实现携程之间的值传递
详细文章
(47条消息) channel 的基本使用_chengqiuming的博客-CSDN博客_channel://ltmh131
package main
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
//复杂操作
println(i)
}
}
复制代码
并发安全
互斥锁
package main
import (
"fmt"
"sync"
)
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
defer wg.Done()
for i := 0; i < 100000; i++ {
lock.Lock()
x++
lock.Unlock()
}
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
复制代码
这样使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒策略是随机的。
读写互斥锁
当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。
读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果获取读锁则会继续获取锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是读锁还是写锁都会等待
package main
import (
"fmt"
"sync"
"time"
)
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
var rwlock sync.RWMutex
func write() {
defer wg.Done()
// lock.Lock() //加互斥锁
rwlock.Lock() //加写锁
x++
time.Sleep(time.Microsecond * 10)
rwlock.Unlock() //解写锁
// lock.Unlock() //解互斥锁
}
func read() {
defer wg.Done()
// lock.Lock() //加互斥锁
rwlock.RLock() //加读锁
time.Sleep(time.Millisecond)
rwlock.RUnlock() //解读锁
// lock.Unlock() //解互斥锁
}
func main() {
start := time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start))
}