This commit is contained in:
github-actions[bot] committed 2022-10-06 07:02:04 +00:00
1 parent e0a177df6c
commit 05d8e1b0bc
4 files changed
+8 -8

No files matched your search

+3 -3
View File
@@ -166,12 +166,12 @@
<h3 id="981-动态栈"><a class="header" href="#981-动态栈">9.8.1. 动态栈</a></h3>
<p>每一个OS线程都有一个固定大小的内存块(一般会是2MB)来做栈,这个栈会用来存储当前正在被调用或挂起(指在调用其它函数时)的函数的内部变量。这个固定大小的栈同时很大又很小。因为2MB的栈对于一个小小的goroutine来说是很大的内存浪费,比如对于我们用到的,一个只是用来WaitGroup之后关闭channel的goroutine来说。而对于go程序来说,同时创建成百上千个goroutine是非常普遍的,如果每一个goroutine都需要这么大的栈的话,那这么多的goroutine就不太可能了。除去大小的问题之外,固定大小的栈对于更复杂或者更深层次的递归函数调用来说显然是不够的。修改固定的大小可以提升空间的利用率,允许创建更多的线程,并且可以允许更深的递归调用,不过这两者是没法同时兼备的。</p>
<p>相反,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。一个goroutine的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量,但是和OS线程不太一样的是,一个goroutine的栈大小并不是固定的;栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB,比传统的固定大小的线程栈要大得多,尽管一般情况下,大多goroutine都不需要这么大的栈。</p>
<p>** 练习 9.4:** 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定)</p>
<p><strong>练习 9.4:</strong> 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定)</p>
<h3 id="982-goroutine调度"><a class="header" href="#982-goroutine调度">9.8.2. Goroutine调度</a></h3>
<p>OS线程会被操作系统内核调度。每几毫秒,一个硬件计时器会中断处理器,这会调用一个叫作scheduler的内核函数。这个函数会挂起当前执行的线程并将它的寄存器内容保存到内存中,检查线程列表并决定下一次哪个线程可以被运行,并从内存中恢复该线程的寄存器信息,然后恢复执行该线程的现场并开始执行线程。因为操作系统线程是被内核所调度,所以从一个线程向另一个“移动”需要完整的上下文切换,也就是说,保存一个用户线程的状态到内存,恢复另一个线程的到寄存器,然后更新调度器的数据结构。这几步操作很慢,因为其局部性很差需要几次内存访问,并且会增加运行的cpu周期。</p>
<p>Go的运行时包含了其自己的调度器,这个调度器使用了一些技术手段,比如m:n调度,因为其会在n个操作系统线程上多工(调度)m个goroutine。Go调度器的工作和内核的调度是相似的,但是这个调度器只关注单独的Go程序中的goroutine(译注:按程序独立)。</p>
<p>和操作系统的线程调度不同的是,Go调度器并不是用一个硬件定时器,而是被Go语言“建筑”本身进行调度的。例如当一个goroutine调用了time.Sleep,或者被channel调用或者mutex操作阻塞时,调度器会使其进入休眠并开始执行另一个goroutine,直到时机到了再去唤醒第一个goroutine。因为这种调度方式不需要进入内核的上下文,所以重新调度一个goroutine比调度一个线程代价要低得多。</p>
<p>** 练习 9.5: ** 写一个有两个goroutine的程序,两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信?</p>
<p><strong>练习 9.5:</strong> 写一个有两个goroutine的程序,两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信?</p>
<h3 id="983-gomaxprocs"><a class="header" href="#983-gomaxprocs">9.8.3. GOMAXPROCS</a></h3>
<p>Go的调度器使用了一个叫做GOMAXPROCS的变量来决定会有多少个操作系统的线程同时执行Go的代码。其默认的值是运行机器上的CPU的核心数,所以在一个有8个核心的机器上时,调度器一次会在8个OS线程上去调度GO代码。(GOMAXPROCS是前面说的m:n调度中的n)。在休眠中的或者在通信中被阻塞的goroutine是不需要一个对应的线程来做调度的。在I/O中或系统调用中或调用非Go语言函数时,是需要一个对应的操作系统线程的,但是GOMAXPROCS并不需要将这几种情况计算在内。</p>
<p>你可以用GOMAXPROCS的环境变量来显式地控制这个参数,或者也可以在运行时用runtime.GOMAXPROCS函数来修改它。我们在下面的小程序中会看到GOMAXPROCS的效果,这个程序会无限打印0和1。</p>
@@ -187,7 +187,7 @@ $ GOMAXPROCS=2 go run hacker-cliché.go
010101010101010101011001100101011010010100110...
</code></pre>
<p>在第一次执行时,最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行,所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。在第二次执行时,我们使用了两个操作系统线程,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的,而runtime也是在不断地发展演进的,所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。</p>
<p>** 练习9.6:** 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心?</p>
<p><strong>练习9.6:</strong> 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心?</p>
<h3 id="984-goroutine没有id号"><a class="header" href="#984-goroutine没有id号">9.8.4. Goroutine没有ID号</a></h3>
<p>在大多数支持多线程的操作系统和程序语言中,当前的线程都有一个独特的身份(id),并且这个身份信息可以以一个普通值的形式被很容易地获取到,典型的可以是一个integer或者指针值。这种情况下我们做一个抽象化的thread-local storage(线程本地存储,多线程编程中不希望其它线程访问的内容)就很容易,只需要以线程的id作为key的一个map就可以解决问题,每一个线程以其id就能从中获取到值,且和其它线程互不冲突。</p>
<p>goroutine没有可以被程序员获取到的身份(id)的概念。这一点是设计上故意而为之,由于thread-local storage总是会被滥用。比如说,一个web server是用一种支持tls的语言实现的,而非常普遍的是很多函数会去寻找HTTP请求的信息,这代表它们就是去其存储层(这个存储层有可能是tls)查找的。这就像是那些过分依赖全局变量的程序一样,会导致一种非健康的“距离外行为”,在这种行为下,一个函数的行为可能并不仅由自己的参数所决定,而是由其所运行在的线程所决定。因此,如果线程本身的身份会改变——比如一些worker线程之类的——那么函数的行为就会变得神秘莫测。</p>
+3 -3
View File
@@ -8051,12 +8051,12 @@ func (e *entry) deliver(response chan&lt;- result) {
<h3 id="981-动态栈"><a class="header" href="#981-动态栈">9.8.1. 动态栈</a></h3>
<p>每一个OS线程都有一个固定大小的内存块(一般会是2MB)来做栈,这个栈会用来存储当前正在被调用或挂起(指在调用其它函数时)的函数的内部变量。这个固定大小的栈同时很大又很小。因为2MB的栈对于一个小小的goroutine来说是很大的内存浪费,比如对于我们用到的,一个只是用来WaitGroup之后关闭channel的goroutine来说。而对于go程序来说,同时创建成百上千个goroutine是非常普遍的,如果每一个goroutine都需要这么大的栈的话,那这么多的goroutine就不太可能了。除去大小的问题之外,固定大小的栈对于更复杂或者更深层次的递归函数调用来说显然是不够的。修改固定的大小可以提升空间的利用率,允许创建更多的线程,并且可以允许更深的递归调用,不过这两者是没法同时兼备的。</p>
<p>相反,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。一个goroutine的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量,但是和OS线程不太一样的是,一个goroutine的栈大小并不是固定的;栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB,比传统的固定大小的线程栈要大得多,尽管一般情况下,大多goroutine都不需要这么大的栈。</p>
<p>** 练习 9.4:** 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定)</p>
<p><strong>练习 9.4:</strong> 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定)</p>
<h3 id="982-goroutine调度"><a class="header" href="#982-goroutine调度">9.8.2. Goroutine调度</a></h3>
<p>OS线程会被操作系统内核调度。每几毫秒,一个硬件计时器会中断处理器,这会调用一个叫作scheduler的内核函数。这个函数会挂起当前执行的线程并将它的寄存器内容保存到内存中,检查线程列表并决定下一次哪个线程可以被运行,并从内存中恢复该线程的寄存器信息,然后恢复执行该线程的现场并开始执行线程。因为操作系统线程是被内核所调度,所以从一个线程向另一个“移动”需要完整的上下文切换,也就是说,保存一个用户线程的状态到内存,恢复另一个线程的到寄存器,然后更新调度器的数据结构。这几步操作很慢,因为其局部性很差需要几次内存访问,并且会增加运行的cpu周期。</p>
<p>Go的运行时包含了其自己的调度器,这个调度器使用了一些技术手段,比如m:n调度,因为其会在n个操作系统线程上多工(调度)m个goroutine。Go调度器的工作和内核的调度是相似的,但是这个调度器只关注单独的Go程序中的goroutine(译注:按程序独立)。</p>
<p>和操作系统的线程调度不同的是,Go调度器并不是用一个硬件定时器,而是被Go语言“建筑”本身进行调度的。例如当一个goroutine调用了time.Sleep,或者被channel调用或者mutex操作阻塞时,调度器会使其进入休眠并开始执行另一个goroutine,直到时机到了再去唤醒第一个goroutine。因为这种调度方式不需要进入内核的上下文,所以重新调度一个goroutine比调度一个线程代价要低得多。</p>
<p>** 练习 9.5: ** 写一个有两个goroutine的程序,两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信?</p>
<p><strong>练习 9.5:</strong> 写一个有两个goroutine的程序,两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信?</p>
<h3 id="983-gomaxprocs"><a class="header" href="#983-gomaxprocs">9.8.3. GOMAXPROCS</a></h3>
<p>Go的调度器使用了一个叫做GOMAXPROCS的变量来决定会有多少个操作系统的线程同时执行Go的代码。其默认的值是运行机器上的CPU的核心数,所以在一个有8个核心的机器上时,调度器一次会在8个OS线程上去调度GO代码。(GOMAXPROCS是前面说的m:n调度中的n)。在休眠中的或者在通信中被阻塞的goroutine是不需要一个对应的线程来做调度的。在I/O中或系统调用中或调用非Go语言函数时,是需要一个对应的操作系统线程的,但是GOMAXPROCS并不需要将这几种情况计算在内。</p>
<p>你可以用GOMAXPROCS的环境变量来显式地控制这个参数,或者也可以在运行时用runtime.GOMAXPROCS函数来修改它。我们在下面的小程序中会看到GOMAXPROCS的效果,这个程序会无限打印0和1。</p>
@@ -8072,7 +8072,7 @@ $ GOMAXPROCS=2 go run hacker-cliché.go
010101010101010101011001100101011010010100110...
</code></pre>
<p>在第一次执行时,最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行,所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。在第二次执行时,我们使用了两个操作系统线程,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的,而runtime也是在不断地发展演进的,所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。</p>
<p>** 练习9.6:** 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心?</p>
<p><strong>练习9.6:</strong> 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心?</p>
<h3 id="984-goroutine没有id号"><a class="header" href="#984-goroutine没有id号">9.8.4. Goroutine没有ID号</a></h3>
<p>在大多数支持多线程的操作系统和程序语言中,当前的线程都有一个独特的身份(id),并且这个身份信息可以以一个普通值的形式被很容易地获取到,典型的可以是一个integer或者指针值。这种情况下我们做一个抽象化的thread-local storage(线程本地存储,多线程编程中不希望其它线程访问的内容)就很容易,只需要以线程的id作为key的一个map就可以解决问题,每一个线程以其id就能从中获取到值,且和其它线程互不冲突。</p>
<p>goroutine没有可以被程序员获取到的身份(id)的概念。这一点是设计上故意而为之,由于thread-local storage总是会被滥用。比如说,一个web server是用一种支持tls的语言实现的,而非常普遍的是很多函数会去寻找HTTP请求的信息,这代表它们就是去其存储层(这个存储层有可能是tls)查找的。这就像是那些过分依赖全局变量的程序一样,会导致一种非健康的“距离外行为”,在这种行为下,一个函数的行为可能并不仅由自己的参数所决定,而是由其所运行在的线程所决定。因此,如果线程本身的身份会改变——比如一些worker线程之类的——那么函数的行为就会变得神秘莫测。</p>
+1 -1
View File
@@ -1 +1 @@
Object.assign(window.search, {"doc_urls":["index.html#go语言圣经中文版","preface.html#前言","preface.html#go语言起源","preface.html#go语言项目","preface.html#本书的组织","preface.html#更多的信息","preface.html#致谢","ch1/ch1.html#第1章-入门","ch1/ch1-01.html#11-hello-world","ch1/ch1-02.html#12-命令行参数","ch1/ch1-03.html#13-查找重复的行","ch1/ch1-04.html#14-gif动画","ch1/ch1-05.html#15-获取url","ch1/ch1-06.html#16-并发获取多个url","ch1/ch1-07.html#17-web服务","ch1/ch1-08.html#18-本章要点","ch2/ch2.html#第2章-程序结构","ch2/ch2-01.html#21-命名","ch2/ch2-02.html#22-声明","ch2/ch2-03.html#23-变量","ch2/ch2-03.html#231-简短变量声明","ch2/ch2-03.html#232-指针","ch2/ch2-03.html#233-new函数","ch2/ch2-03.html#234-变量的生命周期","ch2/ch2-04.html#24-赋值","ch2/ch2-04.html#241-元组赋值","ch2/ch2-04.html#242-可赋值性","ch2/ch2-05.html#25-类型","ch2/ch2-06.html#26-包和文件","ch2/ch2-06.html#261-导入包","ch2/ch2-06.html#262-包的初始化","ch2/ch2-07.html#27-作用域","ch3/ch3.html#第3章-基础数据类型","ch3/ch3-01.html#31-整型","ch3/ch3-02.html#32-浮点数","ch3/ch3-03.html#33-复数","ch3/ch3-04.html#34-布尔型","ch3/ch3-05.html#35-字符串","ch3/ch3-05.html#351-字符串面值","ch3/ch3-05.html#352-unicode","ch3/ch3-05.html#353-utf-8","ch3/ch3-05.html#354-字符串和byte切片","ch3/ch3-05.html#355-字符串和数字的转换","ch3/ch3-06.html#36-常量","ch3/ch3-06.html#361-iota-常量生成器","ch3/ch3-06.html#362-无类型常量","ch4/ch4.html#第4章-复合数据类型","ch4/ch4-01.html#41-数组","ch4/ch4-02.html#42-slice","ch4/ch4-02.html#421-append函数","ch4/ch4-02.html#422-slice内存技巧","ch4/ch4-03.html#43-map","ch4/ch4-04.html#44-结构体","ch4/ch4-04.html#441-结构体字面值","ch4/ch4-04.html#442-结构体比较","ch4/ch4-04.html#443-结构体嵌入和匿名成员","ch4/ch4-05.html#45-json","ch4/ch4-06.html#46-文本和html模板","ch5/ch5.html#第5章-函数","ch5/ch5-01.html#51-函数声明","ch5/ch5-02.html#52-递归","ch5/ch5-03.html#53-多返回值","ch5/ch5-04.html#54-错误","ch5/ch5-04.html#541-错误处理策略","ch5/ch5-04.html#542-文件结尾错误eof","ch5/ch5-05.html#55-函数值","ch5/ch5-06.html#56-匿名函数","ch5/ch5-06.html#561-警告捕获迭代变量","ch5/ch5-07.html#57-可变参数","ch5/ch5-08.html#58-deferred函数","ch5/ch5-09.html#59-panic异常","ch5/ch5-10.html#510-recover捕获异常","ch6/ch6.html#第6章-方法","ch6/ch6-01.html#61-方法声明","ch6/ch6-02.html#62-基于指针对象的方法","ch6/ch6-02.html#621-nil也是一个合法的接收器类型","ch6/ch6-03.html#63-通过嵌入结构体来扩展类型","ch6/ch6-04.html#64-方法值和方法表达式","ch6/ch6-05.html#65-示例-bit数组","ch6/ch6-06.html#66-封装","ch7/ch7.html#第7章-接口","ch7/ch7-01.html#71-接口约定","ch7/ch7-02.html#72-接口类型","ch7/ch7-03.html#73-实现接口的条件","ch7/ch7-04.html#74-flagvalue接口","ch7/ch7-05.html#75--接口值","ch7/ch7-05.html#751--警告一个包含nil指针的接口不是nil接口","ch7/ch7-06.html#76-sortinterface接口","ch7/ch7-07.html#77-httphandler接口","ch7/ch7-08.html#78-error接口","ch7/ch7-09.html#79-示例-表达式求值","ch7/ch7-10.html#710-类型断言","ch7/ch7-11.html#711-基于类型断言区别错误类型","ch7/ch7-12.html#712-通过类型断言询问行为","ch7/ch7-13.html#713-类型分支","ch7/ch7-14.html#714-示例-基于标记的xml解码","ch7/ch7-15.html#715-一些建议","ch8/ch8.html#第8章-goroutines和channels","ch8/ch8-01.html#81-goroutines","ch8/ch8-02.html#82-示例-并发的clock服务","ch8/ch8-03.html#83-示例-并发的echo服务","ch8/ch8-04.html#84-channels","ch8/ch8-04.html#841-不带缓存的channels","ch8/ch8-04.html#842-串联的channelspipeline","ch8/ch8-04.html#843-单方向的channel","ch8/ch8-04.html#844-带缓存的channels","ch8/ch8-05.html#85-并发的循环","ch8/ch8-06.html#86-示例-并发的web爬虫","ch8/ch8-07.html#87-基于select的多路复用","ch8/ch8-09.html#89-并发的退出","ch8/ch8-10.html#810-示例-聊天服务","ch9/ch9.html#第9章-基于共享变量的并发","ch9/ch9-01.html#91-竞争条件","ch9/ch9-02.html#92-syncmutex互斥锁","ch9/ch9-03.html#93-syncrwmutex读写锁","ch9/ch9-04.html#94-内存同步","ch9/ch9-06.html#96-竞争条件检测","ch9/ch9-07.html#97-示例-并发的非阻塞缓存","ch9/ch9-08.html#98-goroutines和线程","ch9/ch9-08.html#981-动态栈","ch9/ch9-08.html#982-goroutine调度","ch9/ch9-08.html#983-gomaxprocs","ch9/ch9-08.html#984-goroutine没有id号","ch10/ch10.html#第10章-包和工具","ch10/ch10-01.html#101-包简介","ch10/ch10-02.html#102-导入路径","ch10/ch10-03.html#103-包声明","ch10/ch10-04.html#104-导入声明","ch10/ch10-05.html#105-包的匿名导入","ch10/ch10-06.html#106-包和命名","ch10/ch10-07.html#107-工具","ch10/ch10-07.html#1071-工作区结构","ch10/ch10-07.html#1072-下载包Line truncated
Object.assign(window.search, {"doc_urls":["index.html#go语言圣经中文版","preface.html#前言","preface.html#go语言起源","preface.html#go语言项目","preface.html#本书的组织","preface.html#更多的信息","preface.html#致谢","ch1/ch1.html#第1章-入门","ch1/ch1-01.html#11-hello-world","ch1/ch1-02.html#12-命令行参数","ch1/ch1-03.html#13-查找重复的行","ch1/ch1-04.html#14-gif动画","ch1/ch1-05.html#15-获取url","ch1/ch1-06.html#16-并发获取多个url","ch1/ch1-07.html#17-web服务","ch1/ch1-08.html#18-本章要点","ch2/ch2.html#第2章-程序结构","ch2/ch2-01.html#21-命名","ch2/ch2-02.html#22-声明","ch2/ch2-03.html#23-变量","ch2/ch2-03.html#231-简短变量声明","ch2/ch2-03.html#232-指针","ch2/ch2-03.html#233-new函数","ch2/ch2-03.html#234-变量的生命周期","ch2/ch2-04.html#24-赋值","ch2/ch2-04.html#241-元组赋值","ch2/ch2-04.html#242-可赋值性","ch2/ch2-05.html#25-类型","ch2/ch2-06.html#26-包和文件","ch2/ch2-06.html#261-导入包","ch2/ch2-06.html#262-包的初始化","ch2/ch2-07.html#27-作用域","ch3/ch3.html#第3章-基础数据类型","ch3/ch3-01.html#31-整型","ch3/ch3-02.html#32-浮点数","ch3/ch3-03.html#33-复数","ch3/ch3-04.html#34-布尔型","ch3/ch3-05.html#35-字符串","ch3/ch3-05.html#351-字符串面值","ch3/ch3-05.html#352-unicode","ch3/ch3-05.html#353-utf-8","ch3/ch3-05.html#354-字符串和byte切片","ch3/ch3-05.html#355-字符串和数字的转换","ch3/ch3-06.html#36-常量","ch3/ch3-06.html#361-iota-常量生成器","ch3/ch3-06.html#362-无类型常量","ch4/ch4.html#第4章-复合数据类型","ch4/ch4-01.html#41-数组","ch4/ch4-02.html#42-slice","ch4/ch4-02.html#421-append函数","ch4/ch4-02.html#422-slice内存技巧","ch4/ch4-03.html#43-map","ch4/ch4-04.html#44-结构体","ch4/ch4-04.html#441-结构体字面值","ch4/ch4-04.html#442-结构体比较","ch4/ch4-04.html#443-结构体嵌入和匿名成员","ch4/ch4-05.html#45-json","ch4/ch4-06.html#46-文本和html模板","ch5/ch5.html#第5章-函数","ch5/ch5-01.html#51-函数声明","ch5/ch5-02.html#52-递归","ch5/ch5-03.html#53-多返回值","ch5/ch5-04.html#54-错误","ch5/ch5-04.html#541-错误处理策略","ch5/ch5-04.html#542-文件结尾错误eof","ch5/ch5-05.html#55-函数值","ch5/ch5-06.html#56-匿名函数","ch5/ch5-06.html#561-警告捕获迭代变量","ch5/ch5-07.html#57-可变参数","ch5/ch5-08.html#58-deferred函数","ch5/ch5-09.html#59-panic异常","ch5/ch5-10.html#510-recover捕获异常","ch6/ch6.html#第6章-方法","ch6/ch6-01.html#61-方法声明","ch6/ch6-02.html#62-基于指针对象的方法","ch6/ch6-02.html#621-nil也是一个合法的接收器类型","ch6/ch6-03.html#63-通过嵌入结构体来扩展类型","ch6/ch6-04.html#64-方法值和方法表达式","ch6/ch6-05.html#65-示例-bit数组","ch6/ch6-06.html#66-封装","ch7/ch7.html#第7章-接口","ch7/ch7-01.html#71-接口约定","ch7/ch7-02.html#72-接口类型","ch7/ch7-03.html#73-实现接口的条件","ch7/ch7-04.html#74-flagvalue接口","ch7/ch7-05.html#75--接口值","ch7/ch7-05.html#751--警告一个包含nil指针的接口不是nil接口","ch7/ch7-06.html#76-sortinterface接口","ch7/ch7-07.html#77-httphandler接口","ch7/ch7-08.html#78-error接口","ch7/ch7-09.html#79-示例-表达式求值","ch7/ch7-10.html#710-类型断言","ch7/ch7-11.html#711-基于类型断言区别错误类型","ch7/ch7-12.html#712-通过类型断言询问行为","ch7/ch7-13.html#713-类型分支","ch7/ch7-14.html#714-示例-基于标记的xml解码","ch7/ch7-15.html#715-一些建议","ch8/ch8.html#第8章-goroutines和channels","ch8/ch8-01.html#81-goroutines","ch8/ch8-02.html#82-示例-并发的clock服务","ch8/ch8-03.html#83-示例-并发的echo服务","ch8/ch8-04.html#84-channels","ch8/ch8-04.html#841-不带缓存的channels","ch8/ch8-04.html#842-串联的channelspipeline","ch8/ch8-04.html#843-单方向的channel","ch8/ch8-04.html#844-带缓存的channels","ch8/ch8-05.html#85-并发的循环","ch8/ch8-06.html#86-示例-并发的web爬虫","ch8/ch8-07.html#87-基于select的多路复用","ch8/ch8-09.html#89-并发的退出","ch8/ch8-10.html#810-示例-聊天服务","ch9/ch9.html#第9章-基于共享变量的并发","ch9/ch9-01.html#91-竞争条件","ch9/ch9-02.html#92-syncmutex互斥锁","ch9/ch9-03.html#93-syncrwmutex读写锁","ch9/ch9-04.html#94-内存同步","ch9/ch9-06.html#96-竞争条件检测","ch9/ch9-07.html#97-示例-并发的非阻塞缓存","ch9/ch9-08.html#98-goroutines和线程","ch9/ch9-08.html#981-动态栈","ch9/ch9-08.html#982-goroutine调度","ch9/ch9-08.html#983-gomaxprocs","ch9/ch9-08.html#984-goroutine没有id号","ch10/ch10.html#第10章-包和工具","ch10/ch10-01.html#101-包简介","ch10/ch10-02.html#102-导入路径","ch10/ch10-03.html#103-包声明","ch10/ch10-04.html#104-导入声明","ch10/ch10-05.html#105-包的匿名导入","ch10/ch10-06.html#106-包和命名","ch10/ch10-07.html#107-工具","ch10/ch10-07.html#1071-工作区结构","ch10/ch10-07.html#1072-下载包Line truncated
+1 -1
View File
@@ -1 +1 @@
{"doc_urls":["index.html#go语言圣经中文版","preface.html#前言","preface.html#go语言起源","preface.html#go语言项目","preface.html#本书的组织","preface.html#更多的信息","preface.html#致谢","ch1/ch1.html#第1章-入门","ch1/ch1-01.html#11-hello-world","ch1/ch1-02.html#12-命令行参数","ch1/ch1-03.html#13-查找重复的行","ch1/ch1-04.html#14-gif动画","ch1/ch1-05.html#15-获取url","ch1/ch1-06.html#16-并发获取多个url","ch1/ch1-07.html#17-web服务","ch1/ch1-08.html#18-本章要点","ch2/ch2.html#第2章-程序结构","ch2/ch2-01.html#21-命名","ch2/ch2-02.html#22-声明","ch2/ch2-03.html#23-变量","ch2/ch2-03.html#231-简短变量声明","ch2/ch2-03.html#232-指针","ch2/ch2-03.html#233-new函数","ch2/ch2-03.html#234-变量的生命周期","ch2/ch2-04.html#24-赋值","ch2/ch2-04.html#241-元组赋值","ch2/ch2-04.html#242-可赋值性","ch2/ch2-05.html#25-类型","ch2/ch2-06.html#26-包和文件","ch2/ch2-06.html#261-导入包","ch2/ch2-06.html#262-包的初始化","ch2/ch2-07.html#27-作用域","ch3/ch3.html#第3章-基础数据类型","ch3/ch3-01.html#31-整型","ch3/ch3-02.html#32-浮点数","ch3/ch3-03.html#33-复数","ch3/ch3-04.html#34-布尔型","ch3/ch3-05.html#35-字符串","ch3/ch3-05.html#351-字符串面值","ch3/ch3-05.html#352-unicode","ch3/ch3-05.html#353-utf-8","ch3/ch3-05.html#354-字符串和byte切片","ch3/ch3-05.html#355-字符串和数字的转换","ch3/ch3-06.html#36-常量","ch3/ch3-06.html#361-iota-常量生成器","ch3/ch3-06.html#362-无类型常量","ch4/ch4.html#第4章-复合数据类型","ch4/ch4-01.html#41-数组","ch4/ch4-02.html#42-slice","ch4/ch4-02.html#421-append函数","ch4/ch4-02.html#422-slice内存技巧","ch4/ch4-03.html#43-map","ch4/ch4-04.html#44-结构体","ch4/ch4-04.html#441-结构体字面值","ch4/ch4-04.html#442-结构体比较","ch4/ch4-04.html#443-结构体嵌入和匿名成员","ch4/ch4-05.html#45-json","ch4/ch4-06.html#46-文本和html模板","ch5/ch5.html#第5章-函数","ch5/ch5-01.html#51-函数声明","ch5/ch5-02.html#52-递归","ch5/ch5-03.html#53-多返回值","ch5/ch5-04.html#54-错误","ch5/ch5-04.html#541-错误处理策略","ch5/ch5-04.html#542-文件结尾错误eof","ch5/ch5-05.html#55-函数值","ch5/ch5-06.html#56-匿名函数","ch5/ch5-06.html#561-警告捕获迭代变量","ch5/ch5-07.html#57-可变参数","ch5/ch5-08.html#58-deferred函数","ch5/ch5-09.html#59-panic异常","ch5/ch5-10.html#510-recover捕获异常","ch6/ch6.html#第6章-方法","ch6/ch6-01.html#61-方法声明","ch6/ch6-02.html#62-基于指针对象的方法","ch6/ch6-02.html#621-nil也是一个合法的接收器类型","ch6/ch6-03.html#63-通过嵌入结构体来扩展类型","ch6/ch6-04.html#64-方法值和方法表达式","ch6/ch6-05.html#65-示例-bit数组","ch6/ch6-06.html#66-封装","ch7/ch7.html#第7章-接口","ch7/ch7-01.html#71-接口约定","ch7/ch7-02.html#72-接口类型","ch7/ch7-03.html#73-实现接口的条件","ch7/ch7-04.html#74-flagvalue接口","ch7/ch7-05.html#75--接口值","ch7/ch7-05.html#751--警告一个包含nil指针的接口不是nil接口","ch7/ch7-06.html#76-sortinterface接口","ch7/ch7-07.html#77-httphandler接口","ch7/ch7-08.html#78-error接口","ch7/ch7-09.html#79-示例-表达式求值","ch7/ch7-10.html#710-类型断言","ch7/ch7-11.html#711-基于类型断言区别错误类型","ch7/ch7-12.html#712-通过类型断言询问行为","ch7/ch7-13.html#713-类型分支","ch7/ch7-14.html#714-示例-基于标记的xml解码","ch7/ch7-15.html#715-一些建议","ch8/ch8.html#第8章-goroutines和channels","ch8/ch8-01.html#81-goroutines","ch8/ch8-02.html#82-示例-并发的clock服务","ch8/ch8-03.html#83-示例-并发的echo服务","ch8/ch8-04.html#84-channels","ch8/ch8-04.html#841-不带缓存的channels","ch8/ch8-04.html#842-串联的channelspipeline","ch8/ch8-04.html#843-单方向的channel","ch8/ch8-04.html#844-带缓存的channels","ch8/ch8-05.html#85-并发的循环","ch8/ch8-06.html#86-示例-并发的web爬虫","ch8/ch8-07.html#87-基于select的多路复用","ch8/ch8-09.html#89-并发的退出","ch8/ch8-10.html#810-示例-聊天服务","ch9/ch9.html#第9章-基于共享变量的并发","ch9/ch9-01.html#91-竞争条件","ch9/ch9-02.html#92-syncmutex互斥锁","ch9/ch9-03.html#93-syncrwmutex读写锁","ch9/ch9-04.html#94-内存同步","ch9/ch9-06.html#96-竞争条件检测","ch9/ch9-07.html#97-示例-并发的非阻塞缓存","ch9/ch9-08.html#98-goroutines和线程","ch9/ch9-08.html#981-动态栈","ch9/ch9-08.html#982-goroutine调度","ch9/ch9-08.html#983-gomaxprocs","ch9/ch9-08.html#984-goroutine没有id号","ch10/ch10.html#第10章-包和工具","ch10/ch10-01.html#101-包简介","ch10/ch10-02.html#102-导入路径","ch10/ch10-03.html#103-包声明","ch10/ch10-04.html#104-导入声明","ch10/ch10-05.html#105-包的匿名导入","ch10/ch10-06.html#106-包和命名","ch10/ch10-07.html#107-工具","ch10/ch10-07.html#1071-工作区结构","ch10/ch10-07.html#1072-下载包","ch10/ch10-07.html#1073-构Line truncated
{"doc_urls":["index.html#go语言圣经中文版","preface.html#前言","preface.html#go语言起源","preface.html#go语言项目","preface.html#本书的组织","preface.html#更多的信息","preface.html#致谢","ch1/ch1.html#第1章-入门","ch1/ch1-01.html#11-hello-world","ch1/ch1-02.html#12-命令行参数","ch1/ch1-03.html#13-查找重复的行","ch1/ch1-04.html#14-gif动画","ch1/ch1-05.html#15-获取url","ch1/ch1-06.html#16-并发获取多个url","ch1/ch1-07.html#17-web服务","ch1/ch1-08.html#18-本章要点","ch2/ch2.html#第2章-程序结构","ch2/ch2-01.html#21-命名","ch2/ch2-02.html#22-声明","ch2/ch2-03.html#23-变量","ch2/ch2-03.html#231-简短变量声明","ch2/ch2-03.html#232-指针","ch2/ch2-03.html#233-new函数","ch2/ch2-03.html#234-变量的生命周期","ch2/ch2-04.html#24-赋值","ch2/ch2-04.html#241-元组赋值","ch2/ch2-04.html#242-可赋值性","ch2/ch2-05.html#25-类型","ch2/ch2-06.html#26-包和文件","ch2/ch2-06.html#261-导入包","ch2/ch2-06.html#262-包的初始化","ch2/ch2-07.html#27-作用域","ch3/ch3.html#第3章-基础数据类型","ch3/ch3-01.html#31-整型","ch3/ch3-02.html#32-浮点数","ch3/ch3-03.html#33-复数","ch3/ch3-04.html#34-布尔型","ch3/ch3-05.html#35-字符串","ch3/ch3-05.html#351-字符串面值","ch3/ch3-05.html#352-unicode","ch3/ch3-05.html#353-utf-8","ch3/ch3-05.html#354-字符串和byte切片","ch3/ch3-05.html#355-字符串和数字的转换","ch3/ch3-06.html#36-常量","ch3/ch3-06.html#361-iota-常量生成器","ch3/ch3-06.html#362-无类型常量","ch4/ch4.html#第4章-复合数据类型","ch4/ch4-01.html#41-数组","ch4/ch4-02.html#42-slice","ch4/ch4-02.html#421-append函数","ch4/ch4-02.html#422-slice内存技巧","ch4/ch4-03.html#43-map","ch4/ch4-04.html#44-结构体","ch4/ch4-04.html#441-结构体字面值","ch4/ch4-04.html#442-结构体比较","ch4/ch4-04.html#443-结构体嵌入和匿名成员","ch4/ch4-05.html#45-json","ch4/ch4-06.html#46-文本和html模板","ch5/ch5.html#第5章-函数","ch5/ch5-01.html#51-函数声明","ch5/ch5-02.html#52-递归","ch5/ch5-03.html#53-多返回值","ch5/ch5-04.html#54-错误","ch5/ch5-04.html#541-错误处理策略","ch5/ch5-04.html#542-文件结尾错误eof","ch5/ch5-05.html#55-函数值","ch5/ch5-06.html#56-匿名函数","ch5/ch5-06.html#561-警告捕获迭代变量","ch5/ch5-07.html#57-可变参数","ch5/ch5-08.html#58-deferred函数","ch5/ch5-09.html#59-panic异常","ch5/ch5-10.html#510-recover捕获异常","ch6/ch6.html#第6章-方法","ch6/ch6-01.html#61-方法声明","ch6/ch6-02.html#62-基于指针对象的方法","ch6/ch6-02.html#621-nil也是一个合法的接收器类型","ch6/ch6-03.html#63-通过嵌入结构体来扩展类型","ch6/ch6-04.html#64-方法值和方法表达式","ch6/ch6-05.html#65-示例-bit数组","ch6/ch6-06.html#66-封装","ch7/ch7.html#第7章-接口","ch7/ch7-01.html#71-接口约定","ch7/ch7-02.html#72-接口类型","ch7/ch7-03.html#73-实现接口的条件","ch7/ch7-04.html#74-flagvalue接口","ch7/ch7-05.html#75--接口值","ch7/ch7-05.html#751--警告一个包含nil指针的接口不是nil接口","ch7/ch7-06.html#76-sortinterface接口","ch7/ch7-07.html#77-httphandler接口","ch7/ch7-08.html#78-error接口","ch7/ch7-09.html#79-示例-表达式求值","ch7/ch7-10.html#710-类型断言","ch7/ch7-11.html#711-基于类型断言区别错误类型","ch7/ch7-12.html#712-通过类型断言询问行为","ch7/ch7-13.html#713-类型分支","ch7/ch7-14.html#714-示例-基于标记的xml解码","ch7/ch7-15.html#715-一些建议","ch8/ch8.html#第8章-goroutines和channels","ch8/ch8-01.html#81-goroutines","ch8/ch8-02.html#82-示例-并发的clock服务","ch8/ch8-03.html#83-示例-并发的echo服务","ch8/ch8-04.html#84-channels","ch8/ch8-04.html#841-不带缓存的channels","ch8/ch8-04.html#842-串联的channelspipeline","ch8/ch8-04.html#843-单方向的channel","ch8/ch8-04.html#844-带缓存的channels","ch8/ch8-05.html#85-并发的循环","ch8/ch8-06.html#86-示例-并发的web爬虫","ch8/ch8-07.html#87-基于select的多路复用","ch8/ch8-09.html#89-并发的退出","ch8/ch8-10.html#810-示例-聊天服务","ch9/ch9.html#第9章-基于共享变量的并发","ch9/ch9-01.html#91-竞争条件","ch9/ch9-02.html#92-syncmutex互斥锁","ch9/ch9-03.html#93-syncrwmutex读写锁","ch9/ch9-04.html#94-内存同步","ch9/ch9-06.html#96-竞争条件检测","ch9/ch9-07.html#97-示例-并发的非阻塞缓存","ch9/ch9-08.html#98-goroutines和线程","ch9/ch9-08.html#981-动态栈","ch9/ch9-08.html#982-goroutine调度","ch9/ch9-08.html#983-gomaxprocs","ch9/ch9-08.html#984-goroutine没有id号","ch10/ch10.html#第10章-包和工具","ch10/ch10-01.html#101-包简介","ch10/ch10-02.html#102-导入路径","ch10/ch10-03.html#103-包声明","ch10/ch10-04.html#104-导入声明","ch10/ch10-05.html#105-包的匿名导入","ch10/ch10-06.html#106-包和命名","ch10/ch10-07.html#107-工具","ch10/ch10-07.html#1071-工作区结构","ch10/ch10-07.html#1072-下载包","ch10/ch10-07.html#1073-构Line truncated