英文:
Go Package Initialization
问题
Situation:
一个Go包A由3个.go
文件组成,我在每个文件中使用另一个包B中的函数。我必须在每个文件的开头导入包B。
Question:
包B实际上是被初始化了3次还是只有1次?
英文:
Situation:
A Go package A is composed of 3 .go
files, and I use functions from another package B in each of these files. I have to import package B at the beginning of each file.
Question:
Is package B actually initialized 3 times or only 1 time?
答案1
得分: 38
简短回答:初始化只会执行一次。
详细回答:引用相关的规范部分 - 程序执行:
> 没有导入的包通过为其所有的包级变量分配初始值,然后调用其源代码中定义的具有以下名称和签名的任何包级函数进行初始化:
>
> func init()
>
> 一个名为init
的包级或文件级标识符只能声明为具有此签名的函数。可以定义多个这样的函数,即使在单个源文件中也可以;它们的执行顺序是未指定的。
>
> 在一个包内,包级变量的初始化和常量值的确定是根据引用顺序进行的:如果A的初始化器依赖于B,那么B将在A之前设置。依赖分析不依赖于正在初始化的项的实际值,只依赖于它们在源代码中的出现。如果A的值包含对B的提及,包含一个初始化器提及B的值,或者提及一个提及B的函数(递归地),则A依赖于B。如果这样的依赖形成一个循环,则会出现错误。如果两个项没有相互依赖,它们将按照它们在源代码中的出现顺序进行初始化,可能在多个文件中,按照编译器的顺序。由于依赖分析是按包进行的,如果A的初始化器调用在另一个包中定义的引用B的函数,它可能会产生未指定的结果。
>
> 不能从程序的任何地方引用init
函数。特别地,不能显式地调用init
,也不能将指向init
的指针分配给函数变量。
>
> 如果一个包有导入,那么导入的包会在初始化该包本身之前进行初始化。如果多个包导入包P,那么P只会被初始化一次。
>
> 通过构造导入包,可以保证初始化中不存在循环依赖。
>
> 一个完整的程序通过将一个名为_main package_的未导入包与其导入的所有包进行传递性链接来创建。主包必须具有包名main
并声明一个不带参数且不返回值的函数main
。
>
> func main() { … }
>
> 程序的执行从初始化主包开始,然后调用函数main
。当函数main
返回时,程序退出。它不会等待其他(非主)goroutine完成。
>
> 包初始化 - 变量初始化和init
函数的调用 - 在单个goroutine中顺序进行,一个包一个接一个地进行。init
函数可以启动其他goroutine,这些goroutine可以与初始化代码并发运行。然而,初始化始终按顺序进行init
函数:在前一个init
函数返回之前,不会启动下一个init
函数。
英文:
Short answer: Initialization will be performed only once.
Long answer: Quoting the relevant specification section - Program execution:
> A package with no imports is initialized by assigning initial values to all its package-level variables and then calling any package-level function with the name and signature of
>
> func init()
>
> defined in its source. A package-scope or file-scope identifier with name init
may only be declared to be a function with this signature. Multiple such functions may be defined, even within a single source file; they execute in unspecified order.
>
> Within a package, package-level variables are initialized, and constant values are determined, according to order of reference: if the initializer of A depends on B, A will be set after B. Dependency analysis does not depend on the actual values of the items being initialized, only on their appearance in the source. A depends on B if the value of A contains a mention of B, contains a value whose initializer mentions B, or mentions a function that mentions B, recursively. It is an error if such dependencies form a cycle. If two items are not interdependent, they will be initialized in the order they appear in the source, possibly in multiple files, as presented to the compiler. Since the dependency analysis is done per package, it can produce unspecified results if A's initializer calls a function defined in another package that refers to B.
>
> An init
function cannot be referred to from anywhere in a program. In particular, init
cannot be called explicitly, nor can a pointer to init
be assigned to a function variable.
>
> If a package has imports, the imported packages are initialized before initializing the package itself. If multiple packages import a package P, P will be initialized only once.
>
> The importing of packages, by construction, guarantees that there can be no cyclic dependencies in initialization.
>
> A complete program is created by linking a single, unimported package called the main package with all the packages it imports, transitively. The main package must have package name main
and declare a function main
that takes no arguments and returns no value.
>
> func main() { … }
>
> Program execution begins by initializing the main package and then invoking the function main
. When the function main
returns, the program exits. It does not wait for other (non-main) goroutines to complete.
>
> Package initialization—variable initialization and the invocation of init
functions—happens in a single goroutine, sequentially, one package at a time. An init
function may launch other goroutines, which can run concurrently with the initialization code. However, initialization always sequences the init
functions: it will not start the next init
until the previous one has returned.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论