Go Once: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(8 intermediate revisions by the same user not shown)
Line 5: Line 5:
<code>Once</code> is a Go synchronization construct that is used when a piece of code must be executed only once. This is a useful idiom in initialization. Note that the code is executed synchronously, when it is executed: the invocation to <code>Once#Do()</code> executes in the same goroutine that makes the <code>Once#Do()</code> invocation.
<code>Once</code> is a Go synchronization construct that is used when a piece of code must be executed only once. This is a useful idiom in initialization. Note that the code is executed synchronously, when it is executed: the invocation to <code>Once#Do()</code> executes in the same goroutine that makes the <code>Once#Do()</code> invocation.


=Example=
This pattern is thread-safe when the initialization is attempted by more than one goroutine.
 
It is recommended that any usage of <code>sync.Once</code> is wrapped in a small lexical block, either a small function or a type.
<syntaxhighlight lang='go'>
var someOnce sync.Once
 
// someFunc has no parameters
func someFunc() {
  ...
}
 
...
someOnce.Do(someFunc) // no parameters are passed
</syntaxhighlight>
 
=Initialization Pattern=


<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
Line 14: Line 29:
func GetLog() {
func GetLog() {
   initLogOnce.Do(func() {
   initLogOnce.Do(func() {
     log = ... // init the log
     log = ... // thread-safely init the log
   })
   })
   return log // because the function passed to Do() executes synchronously, log will be initialized here.
   return log // because the function passed to Do() executes synchronously, log will be initialized here.
Line 23: Line 38:
==<tt>Do()</tt>==
==<tt>Do()</tt>==
The only argument of <code>Do()</code> is a function value, not a function invocation. <code>Do()</code> will use the function value to invoke the function, internally. All invokers will block, except one, which executes synchronously in the same goroutine that performed the <code>Do()</code> invocation. The blocked invokers will be released after the one that does initialization finishes executing, and none of them will execute.
The only argument of <code>Do()</code> is a function value, not a function invocation. <code>Do()</code> will use the function value to invoke the function, internally. All invokers will block, except one, which executes synchronously in the same goroutine that performed the <code>Do()</code> invocation. The blocked invokers will be released after the one that does initialization finishes executing, and none of them will execute.
Note that <code>Do()</code> will execute only once, ever, not only once per unique function it is invoked with. <code>Do()</code> counts the number of times it is called, not how many times unique functions were passed to it.
<font color=darkkhaki>How to invoke a function that has arguments? The only solution I found so far was to wrap into the definition of an anonymous function:
<syntaxhighlight lang='go'>
someOnce.Do(func() {
  someArg := ...
  theFunctionThatMustBeRunOnlyOnce(someArg)
})
</syntaxhighlight>
</font>

Latest revision as of 01:07, 15 March 2024

Internal

Overview

Once is a Go synchronization construct that is used when a piece of code must be executed only once. This is a useful idiom in initialization. Note that the code is executed synchronously, when it is executed: the invocation to Once#Do() executes in the same goroutine that makes the Once#Do() invocation.

This pattern is thread-safe when the initialization is attempted by more than one goroutine.

It is recommended that any usage of sync.Once is wrapped in a small lexical block, either a small function or a type.

var someOnce sync.Once

// someFunc has no parameters
func someFunc() {
  ...
}

...
someOnce.Do(someFunc) // no parameters are passed

Initialization Pattern

var log Log
var initLogOnce sync.Once

...
func GetLog() {
  initLogOnce.Do(func() {
     log = ... // thread-safely init the log
  })
  return log // because the function passed to Do() executes synchronously, log will be initialized here.
}

Methods

Do()

The only argument of Do() is a function value, not a function invocation. Do() will use the function value to invoke the function, internally. All invokers will block, except one, which executes synchronously in the same goroutine that performed the Do() invocation. The blocked invokers will be released after the one that does initialization finishes executing, and none of them will execute.

Note that Do() will execute only once, ever, not only once per unique function it is invoked with. Do() counts the number of times it is called, not how many times unique functions were passed to it.

How to invoke a function that has arguments? The only solution I found so far was to wrap into the definition of an anonymous function:

someOnce.Do(func() {
  someArg := ...
  theFunctionThatMustBeRunOnlyOnce(someArg)
})