Sync. Map Source Analysis

Source: Internet
Author: User
# # [Blog address: Sync. Map Source analysis] (Https://github.com/Chasiny/Blog/blob/master/blog/go/sync. MAP%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.MD) # # Ordinary Mapgo Common map is not supported concurrency, such as simple write ' Gofunc main () {wg: = sync. WAITGROUP{}WG. Add (Ten) M: = Make (Map[int]int) for I: = 0; I < 10; i++ {go func (i int) {M[i] = IWG. Done ()} (i)}WG. Wait ()} "" "Bashfatal error:concurrent Map writes" # # sync. Mapgo Sync.map Several optimization points * Reduce the conflict of locks by using the preferred read fabric read * Use dual detection * Use deferred deletion (delete data that exists in read just set it to nil) * Dynamic adjustment, after Miss number of times, Promote the dirty data to read from the Sync/map.go view map structure "' gotype map struct {mu Mutex//Mutex for locking dirty Mapread atomic. Value//Read map, actually not read-only, is a priority read Dirty Map[interface{}]*entry//dirty is a current up-to-date map that allows read-write misses int//mark the number of misses in read, When misses equals the length of dirty, the dirty is copied to the actual structure of the Read}//read store type readOnly struct {m map[interface{}]*entry//mapamended bool// If some data is in dirty but not in read, the value is True}type entry struct {p unsafe. Pointer//Data Pointer} ' # # # # # Entry of several types * nil: expressed as deleted, at this time, read and dirty have the same key (usually the key value if it exists in read, then delete is to mark it as Nil) * Expunged: also means to be deleted, But the key is only in read and not in dirty, which occurs when you copy read to dirty, that is, the copy process will firstNil is marked as expunged, and then it is not copied to dirty* other: represents the real data # # sync. Map several methods: First of all, read and dirty is not directly stored object, but to save the pointer, so if the key value exists in both read and dirty, direct atom modification Read is equivalent to modify the value in dirty, and when read and dirty have a large amount of the same data and will not use too much memory # # # load ' Gofunc (M *map) load (key interface{}) (Value interface{}, OK bool) {Read, _: = M.read.load (). ( ReadOnly) E, OK: = read.m[key]if!ok && read.amended {//If not in read, and dirty has new data, then dirty ()//double check from M.mu.lock, Because it is possible that read just inserts the value in front of the lock read, _ = M.read.load (). (readOnly) e, ok = read.m[key]if!ok && read.amended {//Not in read, then from Dirty take e, OK = m.dirty[key]m.misslocked ()}m.mu. Unlock ()}if!ok {return nil, False}return e.load ()}func (M *map) misslocked () {//no hit count plus one m.misses++if m.misses < Len ( M.dirty) {return}//When no hit count equals dirty size, copy dirty to Readm.read.Store (Readonly{m:m.dirty}) m.dirty = nilm.misses = 0} " Read is used primarily for reading, and each load is read from read, and when read does not exist and amended is true, the misslocked function is executed from the dirty, regardless of whether the key dirty exists, and the function misses+ 1, when the misses equals the size of dirty, the dirty will be copied to read, at this time dirty will be set to nil # # # # delete ' Gofunc (M *map) Delete (key INTERFACe{}) {Read, _: = M.read.load (). ( ReadOnly) E, OK: = read.m[key]if!ok && read.amended {//If not in read, and dirty has new data, then find dirty ()//double check from M.mu.lock read, _ = M.read.load (). (readOnly) e, ok = read.m[key]if!ok && read.amended {//This is to indicate that the key value exists only in the dirty, delete the key value in the dirty directly deletes (M.dirty, key)} M.mu.unlock ()} If OK {//If in read, mark it as delete (nil) e.delete ()}}func (e *entry) Delete () (hadvalue bool) {for {p: = atomic. Loadpointer (&AMP;E.P) if p = = Nil | | p = = Expunged {return false}if atomic. Compareandswappointer (&AMP;E.P, p, nil) {return True}}} "first determine if you are not in read, and then delete # # # store ' Gofunc (M *map) store from dirty. Key, Value interface{}) {//If Read has this key, and the entry is not marked for deletion, try writing directly to//dirty also points to the entry, so modifying E can also keep dirty up-to-date Entryread, _: = M.read.load (). (readOnly) If e, OK: = Read.m[key]; OK && E.trystore (&value) {return}m.mu.lock () read, _ = M.read.load (). (readOnly) If e, OK: = Read.m[key]; OK {//The key value exists in read if e.unexpungelocked () {//The key value is marked as erased in read, add it to dirtym.dirty[key] = e}//Update entry e.storelocked (& ValUE)} else if e, OK: = M.dirty[key]; OK {//If not in read, in dirty, update e.storelocked (&value)} else {//neither in read nor dirty if!read.amended {// Copy no tag deleted data from read to dirty in m.dirtylocked () M.read.store (READONLY{M:READ.M, Amended:true})}//Add to dirty] = NewEntry (value)}m.mu.unlock ()}func (e *entry) trystore (i *interface{}) bool {p: = atomic. Loadpointer (&AMP;E.P) If p = = expunged {return false}for {if atomic. Compareandswappointer (&AMP;E.P, p, unsafe. Pointer (i)) {return true}p = atomic. Loadpointer (&AMP;E.P) If p = = expunged {return False}}}func (e *entry) unexpungelocked () (wasexpunged bool) {return atomic. Compareandswappointer (&AMP;E.P, expunged, nil)}func (e *entry) storelocked (i *interface{}) {Atomic. Storepointer (&AMP;E.P, unsafe. Pointer (i))}func (M *map) dirtylocked () {if m.dirty! = nil {return}//Copy from read to Dirtyread, _: = M.read.load (). ( readOnly) M.dirty = Make (Map[interface{}]*entry, Len (READ.M)) for k, E: = range READ.M {///If marked as nil or expunged, do not copy to Dirtyif !e.tryexpungelocked () {M.dirTy[k] = E}}}func (e *entry) tryexpungelocked () (isexpunged bool) {p: = atomic. Loadpointer (&AMP;E.P) for p = = nil {//try to reset nil to expungedif Atomic. Compareandswappointer (&AMP;E.P, nil, expunged) {return true}p = atomic. Loadpointer (&AMP;E.P)}return p = = expunged} ' sync. Map writing is a bit of a hassle 1. First will determine whether the key value is already present in read, and then try to write directly (read not just read, write at this time), because the entry pointer from read, so the modification from read reading entry is equivalent to modifying the corresponding entry in dirty, At this point, the use of atomic operations is written. 2. The key value exists in read and the entry is marked as expunged (this occurs when the data is copied from read to dirty, see the tryexpungelocked function, all keys are nil to expunged, indicating that the key is deleted, But not in dirty) 3. From the read copy to Dirty process, the main is implemented with the Dirtylocked function, copy except entry for nil and expunged data---reference * [Go sync. MAP] (https://github.com/golang/go/blob/master/src/sync/map.go) * [Go 1.9 sync. Map Secrets] (http://colobu.com/2017/07/11/dive-into-sync-Map/) 109 reads  
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.