From b2dfdf04ab205b3e68ffd0ab5d36e50e80ddccfd Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 9 Jan 2016 13:15:51 +0800 Subject: [PATCH 1/3] 9.8.3 --- ch9/ch9-08-3.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ch9/ch9-08-3.md b/ch9/ch9-08-3.md index b5f046e..720f996 100644 --- a/ch9/ch9-08-3.md +++ b/ch9/ch9-08-3.md @@ -1,3 +1,23 @@ ### 9.8.3. GOMAXPROCS -TODO +Go的调度器使用了一个叫做GOMAXPROCS的变量来决定会有多少个操作系统的线程同时执行Go的代码。其默认的值是运行机器上的CPU的核心数,所以在一个有8个核心的机器上时,调度器一次会在8个OS线程上去调度GO代码。(GOMAXPROCS是前面说的m:n调度中的n)。在休眠中的或者在通信中被阻塞的goroutine是不需要一个对应的线程来做调度的。在I/O中或系统调用中或调用非Go语言函数时,是需要一个对应的操作系统线程的,但是GOMAXPROCS并不需要将这几种情况计数在内。 + +你可以用GOMAXPROCS的环境变量吕显式地控制这个参数,或者也可以在运行时用runtime.GOMAXPROCS函数来修改它。我们在下面的小程序中会看到GOMAXPROCS的效果,这个程序会无限打印0和1。 + + +```go +for { + go fmt.Print(0) + fmt.Print(1) +} + +$ GOMAXPROCS=1 go run hacker-cliché.go +111111111111111111110000000000000000000011111... + +$ GOMAXPROCS=2 go run hacker-cliché.go +010101010101010101011001100101011010010100110... +``` + +在第一次执行时,最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行,所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。在第二次执行时,我们使用了两个操作系统线程,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的,而runtime也是在不断地发展演进的,所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。 + +练习9.6: 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心? From 27d4a34cae1fe32d5d17be1546e44156eacc1143 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 9 Jan 2016 13:48:53 +0800 Subject: [PATCH 2/3] 9.8 done! --- ch9/ch9-08-4.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ch9/ch9-08-4.md b/ch9/ch9-08-4.md index b0503a8..6851dbc 100644 --- a/ch9/ch9-08-4.md +++ b/ch9/ch9-08-4.md @@ -1,3 +1,10 @@ ### 9.8.4. Goroutine沒有ID號 -TODO +在大多数支持多线程的操作系统和程序语言中,当前的线程都有一个独特的身份(id),并且这个身份信息可以以一个普通值的形式被被很容易地获取到,典型的可以是一个integer或者指针值。这种情况下我们做一个抽象化的thread-local storage(线程本地存储,多线程编程中不希望其它线程访问的内容)就很容易,只需要以线程的id作为key的一个map就可以解决问题,每一个线程以其id就能从中获取到值,且和其它线程互不冲突。 + +goroutine没有可以被程序员获取到的身份(id)的概念。这一点是设计上故意而为之,由于thread-local storage总是会被滥用。比如说,一个web server是用一种支持tls的语言实现的,而非常普遍的是很多函数会去寻找HTTP请求的信息,这代表它们就是去其存储层(这个存储层有可能是tls)查找的。这就像是那些过分依赖全局变量的程序一样,会导致一种非健康的“距离外行为”,在这种行为下,一个函数的行为可能不是由其自己内部的变量所决定,而是由其所运行在的线程所决定。因此,如果线程本身的身份会改变--比如一些worker线程之类的--那么函数的行为就会变得神秘莫测。 + +Go鼓励更为简单的模式,这种模式下参数参函数的影响都是显式的。这样不仅使程序变得更易读,而且会让我们自由地向一些给定的函数分配子任务时不用担心其身份信息。 + +你现在应该已经明白了写一个Go程序所需要的所有语言特性信息。在后面两章节中,我们会回顾一些之前的实例和工具,支持我们写出更大规模的程序:如何将一个工程组织成一系列的包,如果获取,构建,测试,性能测试,剖析,写文档,并且将这些包分享出去。 + From 2b3c0eaa756c83e771488ca5fa9f1f103157a316 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 9 Jan 2016 13:51:19 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E5=88=AB?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch9/ch9-08-4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ch9/ch9-08-4.md b/ch9/ch9-08-4.md index 6851dbc..9dbb38b 100644 --- a/ch9/ch9-08-4.md +++ b/ch9/ch9-08-4.md @@ -4,7 +4,7 @@ goroutine没有可以被程序员获取到的身份(id)的概念。这一点是设计上故意而为之,由于thread-local storage总是会被滥用。比如说,一个web server是用一种支持tls的语言实现的,而非常普遍的是很多函数会去寻找HTTP请求的信息,这代表它们就是去其存储层(这个存储层有可能是tls)查找的。这就像是那些过分依赖全局变量的程序一样,会导致一种非健康的“距离外行为”,在这种行为下,一个函数的行为可能不是由其自己内部的变量所决定,而是由其所运行在的线程所决定。因此,如果线程本身的身份会改变--比如一些worker线程之类的--那么函数的行为就会变得神秘莫测。 -Go鼓励更为简单的模式,这种模式下参数参函数的影响都是显式的。这样不仅使程序变得更易读,而且会让我们自由地向一些给定的函数分配子任务时不用担心其身份信息。 +Go鼓励更为简单的模式,这种模式下参数对函数的影响都是显式的。这样不仅使程序变得更易读,而且会让我们自由地向一些给定的函数分配子任务时不用担心其身份信息影响行为。 你现在应该已经明白了写一个Go程序所需要的所有语言特性信息。在后面两章节中,我们会回顾一些之前的实例和工具,支持我们写出更大规模的程序:如何将一个工程组织成一系列的包,如果获取,构建,测试,性能测试,剖析,写文档,并且将这些包分享出去。