Golang synchronization: Difference between revisions
(5 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 52: | Line 63: | ||
= WaitGroups = | = WaitGroups = | ||
<blockquote> | <blockquote> | ||
WaitGroups are global threadsafe counters that are incremented/decremented. | == Basics == | ||
<blockquote> | |||
WaitGroups are global threadsafe counters that are incremented/decremented.<br> | |||
You can think of them as semaphores. | |||
<syntaxhighlight lang="go"> | <syntaxhighlight lang="go"> | ||
Line 80: | Line 94: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Basics --> | |||
== Debugging == | |||
<blockquote> | |||
Inspecting the waitgroup struct in a debugger can be very helpful.<br> | |||
Notably, you can tell if a WaitGroup has satisfied all wait conditions,<br> | |||
and even have a rough idea how many conditions it is still waiting for. | |||
=== Remaining Waits === | |||
<blockquote> | |||
How to debug, recap | |||
<syntaxhighlight lang="bash"> | |||
dlv test foo.com/x/project/./internal/pkg -- -test.run TestSomething # start debugger | |||
c # start executing | |||
p wg # pring your WaitGroup value | |||
</syntaxhighlight> | |||
Comparing values, you can see <code>state1</code> is multiples or the original value. | |||
<syntaxhighlight lang="go"> | |||
// 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,} | |||
</syntaxhighlight> | |||
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. | |||
</blockquote><!-- Remaining Waits --> | |||
=== Memory Address === | |||
<blockquote> | |||
Within [[golang delve]], you can find out if your object is pointing to the same memory address. | |||
<syntaxhighlight lang="bash"> | |||
p &wg # memory addr of object | |||
p &*wg # memory addr of obj referenced by pointer | |||
</syntaxhighlight> | |||
</blockquote><!-- Memory Address --> | |||
</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