由时间问题产生死锁,代码运行时机: 检测,防止和纠正死锁的四个方式:
相互排斥:并发进程同时拥有资源的独占权等待条件:并发进程必须同时拥有一个资源,并等待额外的资源没有抢占:并发进程拥有的资源只能被该进程释放循环等待:一个并发进程p1必须等待一系列其他并发进程p2,p2也同时在等待p1 上面的代码中goroutine的资源无法做到(没有抢占)活锁:正在主动执行并发操作的程序,但是这些操作不能向前推进程序的状态
//模拟人通过走廊 func main(){ cadence := sync.NewCond(&sync.Mutex{}) go func(){ for range time.Tick(1 *time.Millisecond){ cadence.Broadcast() } }() takeStep := func(){ cadence.L.Lock() cadence.Wait() cadence.L.Unlock() } //tryDir允许一个人尝试一个方向移动 tryDir := func(dirName string, dir *int32, out *bytes.Buffer)bool{ fmt.Fprintf(out,"%v",dirName) atomic.AddInt32(dir,1)//向一个方向移动 takeStep() //每个人每次移动的节奏一样 if atomic.LoadInt32(dir)==1{ fmt.Fprintf(out,".Success!") return true } takeStep() atomic.AddInt32(dir,-1)//表示不能走放弃 return false } var left, right int32 tryLeft := func(out *bytes.Buffer)bool{return tryDir(" left ",&left,out)} tryRight := func(out *bytes.Buffer) bool{return tryDir(" right ",&right,out)} walk := func(walking *sync.WaitGroup, name string){ var out bytes.Buffer defer func() {fmt.Println(out.String())}() defer walking.Done() fmt.Fprintf(&out, "%v is trying to scoot:",name) for i := 0; i < 5; i++{//对尝试次数进行限制 if tryLeft(&out) || tryRight(&out){//首先会尝试向左 return } } fmt.Fprintf(&out,"\n%v hello!",name) } var peopleIn sync.WaitGroup//模拟两个人 peopleIn.Add(2) go walk(&peopleIn,"tom") go walk(&peopleIn,"alice") peopleIn.Wait() } //结果: //alice is trying to scoot: left right left right left right left right left right alice hello! tom is trying to scoot: left right left right left right left right left right tom hello! tom和alice在退出之前会持续竞争饥饿:表示在任何情况下,并发进程都无法获得执行工作所需的所有资源 饥饿通常指一个或多个并发进程占有资源,使得其他进程不能占有资源进行执行
var wg sync.WaitGroup var sharedLock sync.Mutex const runtime = 1 * time.Second greedyWorker := func(){ defer wg.Done() var count int for begin := time.Now(); time.Since(begin) <= runtime; { sharedLock.Lock() time.Sleep(3*time.Nanosecond) sharedLock.Unlock() count++ } fmt.Printf("Greedy worker was able to execute %d work loops\n",count) } politeWorker := func(){ defer wg.Done() var count int for begin := time.Now(); time.Since(begin) <= runtime; { sharedLock.Lock() time.Sleep(1*time.Nanosecond) sharedLock.Unlock() sharedLock.Lock() time.Sleep(1*time.Nanosecond) sharedLock.Unlock() sharedLock.Lock() time.Sleep(1*time.Nanosecond) sharedLock.Unlock() count++ } fmt.Printf("Polite worker was able to execute %d work loops\n",count) } wg.Add(2) go greedyWorker() go politeWorker() wg.Wait() 结果: Greedy worker was able to execute 297 work loops Polite worker was able to execute 99 work loops 从结果中可以看出greedyWorker扩大了其持有共享锁的临界区,并阻止了politeWorker的高效工作饥饿会导致程序性能不佳或错误,有可能使得程序出错,如果上面的例子中greedy完全阻止了polite完成工作,会使得polite永远得不到执行 在进行内存访问同步时,需要在粗粒度同步和细粒度同步之间找到平衡点,以提高程序的性能