您的位置 首页 > 德语词汇

copycheck的(读写锁的nocopy机制)

很多朋友对于copycheck的和读写锁的nocopy机制不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

在go中,每次读写时都需要加互斥锁,这个对程序的影响还是比较大的。所以我们在sync包中能够找到另外一个锁—读写锁。当然,读写锁适用于读次数远远多于写次数的场景。

那么读写锁和互斥锁有什么联系和不同呢?

copycheck的(读写锁的nocopy机制)

typeRWMutexstruct{\n\twMutex\n\twriterSemuint32\n\treaderSemuint32\n\treaderCountint32\n\treaderWaitint32\n}

可以看到w是互斥锁,writerSem和readerSem都是等待者,readerCount是读计数器,readerWait是获取写锁需要等待的读锁释放数量。

而最多支持rwmutexMaxReaders(2^30个读计数器)

这里读写锁做了个很精妙的方法区分读写锁,如果有写锁进来,将readerCount-wmutexMaxReaders,因为readerCount最大数量是小于wmutexMaxReaders,所以在加锁结果过程中,如果发现readerCount<0,那么就知道有写锁加进来了。

每次goroutine获取读锁时,readerCount+1,然后分两种情况:

func(rw*RWMutex)RLock(){\n\tifatomic.AddInt32(&rw.readerCount,1)<0{\n\t\t//将goroutine排到队列尾部,挂起goroutine,监听readerSem信号量\n\t\truntime_SemacquireMutex(&rw.readerSem,false,0)\n\t}\n}2、读解锁

读解锁只会撤销对应的RLock调用,不会影响其他读锁

将readerCount-1,此时分为以下几种情况:

如果r>0,那么直接解锁,而对于r<0的情况,第三种和第四种是异常情况,不能用RUnlock解写锁,只能将readerCount-1,并唤醒等待的读锁,只有将所有读锁的goroutine全部释放,才会唤醒写锁。

func(rw*RWMutex)RUnlock(){\n\tifr:=atomic.AddInt32(&rw.readerCount,-1);r<0{\n\t\t//Outlinedslow-pathtoallowthefast-pathtobeinlined\n\t\trw.rUnlockSlow(r)\n\t}\n}\n\nfunc(rw*RWMutex)rUnlockSlow(rint32){\n\tifr+1==0||r+1==-rwmutexMaxReaders{\n\t\tthrow("sync:RUnlockofunlockedRWMutex")\n\t}\n\tifatomic.AddInt32(&rw.readerWait,-1)==0{\n\t\truntime_Semrelease(&rw.writerSem,false,1)\n\t}\n}3、写加锁

写操作是互斥的,所以写操作是需要添加互斥锁,然后通知其他读锁,如果有读锁,就挂起写锁

func(rw*RWMutex)Lock(){\n\trw.w.Lock()\n\tr:=atomic.AddInt32(&rw.readerCount,-rwmutexMaxReaders)+rwmutexMaxReaders\n\tifr!=0&&atomic.AddInt32(&rw.readerWait,r)!=0{\n\t\truntime_SemacquireMutex(&rw.writerSem,false,0)\n\t}\n}4、写解锁

同样,写解锁向读锁发出通知,还原加锁的readerCount

func(rw*RWMutex)Unlock(){\n\tr:=atomic.AddInt32(&rw.readerCount,rwmutexMaxReaders)\n\tifr>=rwmutexMaxReaders{\n\t\tthrow("sync:UnlockofunlockedRWMutex")\n\t}\n\tfori:=0;i<int(r);i++{\n\t\truntime_Semrelease(&rw.readerSem,false,0)\n\t}\n\trw.w.Unlock()\n}三、nocopy

因为是需要加锁解锁操作,所以在goroutine中是不能使用拷贝的。注释中也明确指定了这一点:

ARWMutexmustnotbecopiedafterfirstuse.

如果我们在代码中使用会出现异常情况。

如果结构体对象包含指针字段,当该对象被拷贝时,会使得两个对象中的指针字段变得不再安全。

typeSstruct{\n\tf1int\n\tf2*s\n}\n\ntypesstruct{\n\tnamestring\n}\n\nfuncmain(){\n\tmOld:=S{\n\t\tf1:0,\n\t\tf2:&s{name:"mike"},\n\t}\n\tmNew:=mOld//拷贝\n\tmNew.f1=1\n\tmNew.f2.name="jane"\n\n\tfmt.Println(mOld.f1,mOld.f2)//输出:0&{jane}\n}

这时修改mNew的字段值会把mOld字段值修改掉,这就可能会引发安全问题。

funcmain(){\n\tvarastrings.Builder\n\ta.Write([]byte("a"))\n\tb:=a\n\tb.Write([]byte("b"))\n}

这段代码运行会报错

panic:strings:illegaluseofnon-zeroBuildercopiedbyvalue

这时因为它在内部实现了copyCheck方法

//Donotcopyanon-zeroBuilder.\ntypeBuilderstruct{\n\taddr*Builder//ofreceiver,todetectcopiesbyvalue\n\tbuf[]byte\n}\n\nfunc(b*Builder)Write(p[]byte)(int,error){\n\tb.copyCheck()\n\tb.buf=append(b.buf,p...)\n\treturnlen(p),nil\n}\n\nfunc(b*Builder)copyCheck(){\n\tifb.addr==nil{\n\t\tb.addr=(*Builder)(noescape(unsafe.Pointer(b)))\n\t}elseifb.addr!=b{\n\t\tpanic("strings:illegaluseofnon-zeroBuildercopiedbyvalue")\n\t}\n}

实现了逻辑也比较简单,b.addr指向了自身的指针,如果将a赋值给b,那么a和b本身是不同的对象,因此b.addr实际会指向a导致panic。

这里的noescape里面就有关于逃逸分析的内容

那有没有更简单的方式呢?有,就是互斥锁的接口

typenoCopystruct{}\nfunc(*noCopy)Lock(){}\nfunc(*noCopy)Unlock(){}

sync包中都存在nocopy检查,通过govet进行copy检查,都是添加这种类型。

也就是说,我们在代码中也使用这种方式,可以进行nocopy检查。

好了,文章到这里就结束啦,如果本次分享的copycheck的和读写锁的nocopy机制问题对您有所帮助,还望关注下本站哦!

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023