Golang synchronization: Difference between revisions
(3 intermediate revisions by the same user not shown) | |||
Line 9: | Line 9: | ||
|} | |} | ||
</blockquote><!-- Documentation --> | </blockquote><!-- Documentation --> | ||
= Atomics = | |||
<blockquote> | |||
<syntaxhighlight lang="go"> | |||
import "sync/atomic" | |||
bool := atomic.Bool{} | |||
prevVal : = bool.Swap(true) // assigns true, returns prev-value | |||
bool.Store(false) // assigns true | |||
</syntaxhighlight> | |||
</blockquote><!-- Atomics --> | |||
= Mutexes = | = Mutexes = | ||
Line 137: | Line 148: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
p &wg | p &wg # memory addr of object | ||
p &*wg # memory addr of obj referenced by pointer | |||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Memory Address --> | </blockquote><!-- Memory Address --> | ||
</blockquote><!-- Debugging --> | </blockquote><!-- Debugging --> | ||
</blockquote><!-- WaitGroups --> | </blockquote><!-- WaitGroups --> |
Latest revision as of 04:32, 17 February 2023
Synchronization tools are used to synchronize multiple concurrent codepaths so that your program can continue synchronously.
Documentation
sync
docshttps://pkg.go.dev/sync
Atomics
import "sync/atomic" bool := atomic.Bool{} prevVal : = bool.Swap(true) // assigns true, returns prev-value bool.Store(false) // assigns true
Mutexes
Mutex
A lock used to synchronize threads. No wait-time is specified, you are responsible for re-trying the lock if desired.
var lock = Mutex{} lock.Lock() lock.Unlock() lock.TryLock() // discouragedRWMutex
Variation of a mutex where:
- any number of items can acquire a lock for reading
- only one item can acquire lock for writing
- you cannot write while any other lock is reading
import "sync" var lock = RWMutex{} // lock/unlock for reading lock.RLock() lock.RUnlock() // lock/unlock for writing lock.Lock() lock.Unlock()It would be sensible to unlcok in
defer
red functions.
WaitGroups
Basics
WaitGroups are global threadsafe counters that are incremented/decremented.
You can think of them as semaphores.wg = sync.WaitGroup{} wg.Add(3) // add 3x increments to countdown wg.Done() // decrement waitgroup by one wg.Wait() // wait for waitgroup to reach 0// wait-groups are threadsafe proxy objects, intended to be globally accessible var wg = sync.WaitGroup{} func printHi() { fmt.Println("hi") wg.Done() } func main() { wg.Add(3) for i=0; i<3; i++ { go printHi() } wg.Wait() fmt.Println("bye") }Debugging
Inspecting the waitgroup struct in a debugger can be very helpful.
Notably, you can tell if a WaitGroup has satisfied all wait conditions,
and even have a rough idea how many conditions it is still waiting for.Remaining Waits
How to debug, recap
dlv test foo.com/x/project/./internal/pkg -- -test.run TestSomething # start debugger c # start executing p wg # pring your WaitGroup valueComparing values, you can see
state1
is multiples or the original value.// new/satisfied waitgroup (4294967296 * 0 == 0) sync.WaitGroup { noCopy: sync.noCopy {}, state1: 0, state2: 1, } // wait 1x 'wg.Done()' (4294967296 * 1 == 4294967296) sync.WaitGroup { noCopy: sync.noCopy {}, state1: 4294967296, state2: 0,} // wait on 2x 'wg.Done()' (4294967296 * 2 == 8589934592) sync.WaitGroup { noCopy: sync.noCopy {}, state1: 8589934592, state2: 0,} // wait on 3x 'wg.Done()' (4294967296 * 3 == 12884901888) sync.WaitGroup { noCopy: sync.noCopy {}, state1: 12884901888, state2: 0,}You can poke in the sourcecode for the details, but even without it you can use this to get a rough idea of what state your waitgroup is in and when.
Memory Address
Within golang delve, you can find out if your object is pointing to the same memory address.
p &wg # memory addr of object p &*wg # memory addr of obj referenced by pointer