go 并发 gorouting chan channel select Mutex sync.One
goroutine
// head: 前缀 index:是一个int的指针
func print(head string, index *int) {for i := 0; i < 5; i++ {// 指针对应的int ++*index++fmt.Println(*index, head, i)// 暂停1stime.Sleep(1 * time.Second)}
}/*
Go 允许使用 go 语句开启一个新的运行期线程,即 goroutine
以一个不同的、新创建的 goroutine 来执行一个函数
同一个程序中的所有 goroutine 共享同一个地址空间。
*/
func main() {fmt.Println("main ...")index := 0go print("first", &index)go print("second", &index)time.Sleep(6 * time.Second)fmt.Println("success ...")
}
chan 一般用法
// 求和,并将数据放在channel中
func sum(arr []int, resultChan chan int) {if len(arr) <= 0 {resultChan <- 0return}var sum = 0for _, value := range arr {sum += value}fmt.Println(sum)// 将结果放在channel中resultChan <- sum
}/*
channel 用于 goroutine之间进行通信1. 创建channelch1 := make(chan 类型) 默认是没有缓冲区的ch2 := make(chan 类型, 缓存长度)2. 添加数据到channel中ch1 <- 1233. 从channel中获取数据var value = <- ch1有无缓冲区区别:
1. 没有缓冲区 按照缓冲区为1来处理,即channel只能放一个数据
2. channel满了后就会阻塞,直到有空位才可以继续放入数据
3. 获取数据类似,阻塞到channel中有数据
*/
func main() {fmt.Println("main ...")// 创建一个没有缓冲区的channelresultChan := make(chan int)array1 := []int{10, 20, 30}array2 := []int{1, 2, 3}// 给两个数组求和并将结果放在channel中go sum(array1, resultChan)go sum(array2, resultChan)// 从channel中获取两个数据,打印到consolefmt.Println(<-resultChan, <-resultChan)fmt.Println("continue ...")// 如果继续获取则会报错:fatal error: all goroutines are asleep - deadlock!// fmt.Println(<-resultChan)fmt.Println("success ...")
}
无缓冲区的chan只能结合goroutine使用
func main() {fmt.Println("main ...")// 创建一个没有缓冲区的channelresultChan := make(chan int)// chan 只能结合goroutine来使用,否则报错// fatal error: all goroutines are asleep - deadlock!resultChan <- 10fmt.Println(<-resultChan)fmt.Println("success ...")
}
有缓冲区的chan可直接赋值
func main() {fmt.Println("main ...")// 创建一个有缓冲区的channelch := make(chan int, 2)ch <- 1ch <- 2fmt.Println(<-ch)fmt.Println(<-ch)fmt.Println("success ...")
}
for rang获取channel数据
func addData(ch chan int, len int) {for i := 0; i < len; i++ {ch <- i}// 如果不关闭,for val := range ch 会阻塞获取数据close(ch)
}/*- 可以使用rang遍历channel,如果channel关闭则直接结束,否则会阻塞等待数据的输入for val := range ch
*/
func main() {fmt.Println("main ...")// 创建一个有缓冲区的channellen := 5ch := make(chan int, len)go addData(ch, len)for val := range ch {fmt.Println(val)}fmt.Println("success ...")
}
select **等待多个goroutine
select可以等待多个goroutine,会阻塞一直到某个case不在阻塞。
func print1(header string, ch1, ch2 chan int) {for i := 0; i < len; i++ {select {case val := <-ch1:fmt.Println(header, val)time.Sleep(time.Second)case ch2 <- i:// do nothing}}
}func main() {fmt.Println("main ...")ch1 := make(chan int)ch2 := make(chan int)go print1("f1", ch1, ch2)go print1("f2", ch2, ch1)time.Sleep(6 * time.Second)fmt.Println("success ...")
}
WaitGroup等待所有goroutine完成
类似java中的CountDownLatch
// 不怎么理解为什么group要用指针
func work(index int, wg *sync.WaitGroup) {defer wg.Done()fmt.Println(index)time.Sleep(time.Second)
}func main() {fmt.Println("main ...")var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)// 为什么要将wg的指针传过去go work(i, &wg)}// 阻塞到所有的goroutine完成后wg.Wait()fmt.Println("success ...")
}
并发锁Mutex
type ConcurrentMap struct {lock sync.Mutexhashmap map[string]int
}// 1. cm *ConcurrentMap 要传指针,否则操作的是副本
// 2. wg *sync.WaitGroup 这个也传指针,确保操作的是一个对象
func (cm *ConcurrentMap) inc(index int, key string, wg *sync.WaitGroup) {defer wg.Done()fmt.Println(index, "start")cm.lock.Lock()before := cm.hashmap[key]fmt.Println(index, "before", before)cm.hashmap[key] = cm.hashmap[key] + 1time.Sleep(time.Microsecond * 100)after := cm.hashmap[key]fmt.Println(index, "after", after)if before+1 != after {fmt.Println(index, "error >>>>>")}cm.lock.Unlock()fmt.Println(index, "end")
}func main() {fmt.Println("main ...")cm := ConcurrentMap{hashmap: make(map[string]int)}var wg sync.WaitGroupfor i := 0; i < 3; i++ {wg.Add(1)go cm.inc(i, "apple", &wg)}wg.Wait()fmt.Println("success ...", cm.hashmap["apple"])
}
RWMutex 读写锁子
参考:
https://www.jianshu.com/p/679041bdaa39
sync.Once 配置文件只加载一次
需求:获取配置文件,如果没有价值只就加载
写法1:
func initMap() {if hasInitMap {return}initMapLock.Lock()defer initMapLock.Unlock()if !hasInitMap {fmt.Println("init map")m = map[string]string{"aaa": "111","bbb": "22",}hasInitMap = true}
}func getValue1(key string) string {initMap()return m[key]
}
写法2:
// 定义一次执行对象
var once sync.Oncefunc initMap2() {m = map[string]string{"aaa": "111","bbb": "22",}
}func getValue2(key string) string {// 一次执行once.Do(initMap2)return m[key]
}func main() {fmt.Println("main ...")for i := 0; i < 20; i++ {fmt.Println(getValue1("aaa"))fmt.Println(getValue2("aaa"))}fmt.Println("success ...")
}
sync.Map 类型ConcurrentHashMap
是安全的Map
atomic.AddInt64(&intV,1) 对基础类型安全操作方法
多线程给变量递增: intV := 3
1. 直接+1 线程不安全
2. 使用Mutex锁代价太大
3. 使用atomic包的方法最好,类似Java中的Atomic
参考
https://blog.csdn.net/weixin_53623989/article/details/136209823
https://blog.csdn.net/e2788666/article/details/130644433