go牛刀小试之互斥锁 vs 状态协程

本文探讨了Go语言中使用互斥锁(Mutex)管理和大并发读写map的场景,与利用状态协程和通道实现的并发通信机制进行对比,揭示了Go内存共享哲学和通道在简化并发管理中的优势。

互斥锁

/*
目的:通过大并发读写同一个map变量,来展示go的大并发能力;
实现:设定go map变量,互斥锁Mutex, 改100个读协程,10个写协程;  atomic纪录读写总次数;
方法:sync.Mutex{},  map [int] int ;
 */

package main
import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

func main() {

	var state = make(map [int] int)
	var mutex = & sync.Mutex{}
	var readTimes uint64
	var writeTimes uint64
	var totalV uint64

	for r := 0; r < 100; r++ {
		go func() {
			/*先从state中读取数值--加-解锁,然后加到总值里面,最后刷新readTImes,延迟 */
			for {
				k := rand.Intn(5)
				mutex.Lock()
				k = state[k]
				mutex.Unlock()
				atomic.AddUint64(& readTimes, 1)
				atomic.AddUint64(& totalV, uint64(k))
				time.Sleep(time.Millisecond)
		    }
		}()
	}

    for w := 0; w < 10; w++ {
    	go func() {
    		for {
				k := rand.Intn(5)
				v := rand.Intn(100)
				mutex.Lock()
				state[k] = v
				mutex.Unlock()
				atomic.AddUint64(&writeTimes, 1)
				time.Sleep(time.Millisecond)
			}
		}()
	}

	time.Sleep(time.Second)
    fmt.Println("Read times is :", atomic.LoadUint64(& readTimes))
    fmt.Println("Write times is ", atomic.LoadUint64(& writeTimes))

    mutex.Lock()
    fmt.Println("State value: ", state)
    mutex.Unlock()

    fmt.Println("Total values is :", atomic.LoadUint64(& totalV))


}

results

Read times is : 57818
Write times is 5781
State value: map[0:19 1:98 2:12 3:63 4:16]
Total values is : 2855694

状态协程

/*
目的:通过协程方法(通道间通信),实现协程间state或值共享;而非互斥锁;
作用:协程方法共享状态,比互斥锁复杂;但是在管理多个互斥锁容易出错,这时候,协程间通信很有优势;
方法:定义状态的管理协程,定义读结构体,写结构体(传递变量和通道 控制), 定义读写协程::
--关键在于读或写协程中,一遍给读写通道输入读写结构体; 同时等待状态协程修改读写通道中的结构体数值;实现类锁机制,简洁易于懂---
 Go 共享内存的思想是,通过通信使每个数据仅被单个协程所拥有,即通过通信实现共享内存。
变量和函数: type name struct{}, make(chan type); atomic.addUint64, atomic.loadUint64;
select操作ch,只会干其一,不会同读同写state;
多并发写: 通过内置ch,保证独占写状态; (这个协程没写完,state协程不返回状态,-state阻塞;而通道会该协程。使得他等待;)
 */

package main
import (
	"fmt"
	"math/rand"
	"sync/atomic"
	"time"
)

type writeStru struct {
	k int
	v int
	resp chan bool
}

type readStru struct {
    k int
    resp chan int
}

func main() {

	writeCh := make(chan writeStru)
	readCh := make(chan readStru)

	var writeNum uint64
	var readNum uint64

	var sumOp uint64
	/*state 协程, state 是其内部独占的量,控制唯一性读写的依据 */
	go func() {
		state :=  make(map[int] int)
		for {//select只是一次性的,需要循环选择
			select {
			case write := <- writeCh:
				 state[write.k] = write.v
				 write.resp <- true //写完后,通过写入true响应通道write方式来让写协程走完;
			case read := <- readCh:
				 v := state[read.k]
				 read.resp <- v //通过返回通道,激活被阻塞的读协程
			}
		}
	}()

	/*写协程 */
    for i:= 0; i < 10; i ++ {
    	go func() {
    		for {
				writeOp := writeStru {
					k : rand.Intn(5),
					v : rand.Intn(100),
					resp : make(chan bool),
				}
				writeCh <- writeOp //将新建的写结构体,通道传递给state修改状态,resp没返回前阻塞
				<- writeOp.resp //阻塞等待 state协程,修改完
				atomic.AddUint64(& writeNum, 1)
				time.Sleep(time.Millisecond)
			}
		}()
	}

	/*读协程*/
    for i := 0; i < 100; i ++ {
    	go func() {
    		for {
				readOp := readStru {
					k : rand.Intn(5),
					resp: make(chan int),
				}
				readCh <- readOp
				val := <- readOp.resp //等待state协程读完,返回值再处理
				atomic.AddUint64(& sumOp, uint64(val))
				atomic.AddUint64(& readNum, 1)
				time.Sleep(time.Millisecond)
			}
		}()
	}
    time.Sleep(time.Second)
    fmt.Println("100个读协程1s总读取次数: ", atomic.LoadUint64(& readNum))
	fmt.Println("100个写协程1s总写入次数: ", atomic.LoadUint64(& writeNum))

	fmt.Println("100个写协程1s累加状态值: ", atomic.LoadUint64(& sumOp))
}



results

100个读协程1s总读取次数: 59901
100个写协程1s总写入次数: 6000
100个写协程1s累加状态值: 3017231

summary

  • 互斥锁应用并发的通用概念,实现容易;var读写前后都mutex.lock .unlock即可;但是多锁的逻辑和问题定位很要命
  • 状态协同,需要设计多个通道在状态协程间修改;需要设计通信通道,再每次读写协程-状态协程间阻塞、切换; 实现较为复杂,但是因为内合了go的通道设计、并发的思想“Go 共享内存的思想是,通过通信使每个数据仅被单个协程所拥有,即通过通信实现共享内存。 基于通道的方法与该思想完全一致! ”
  • 通道是go比较核心的通信、内存共享理念和并发关键点
  • uint64格式需要注意,go是静态强类型语言
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值