golang什么场景设置golang runtime包.GOMAXPROCS=cpu数量会提高性能

更多公众号:GolangwebGo 语言社区专业的Go语言开发社区,除此之外我们还研发了Go 社区APP,让用户随时随地可以了解Go语言的行业动态。最新文章相关作者文章搜狗:感谢您阅读golang一个例子引出的几个问题 本文版权归原作者所有,本文由网友投递产生,如有侵权请联系 ,会第一时间为您处理删除。渔坞 -- 我认识的Golang语言
小渔(aLaxy)
& & & &今天是号,这一个月来最大的天气特色就是湿,空气潮湿,一些原来看着很干净的东西开始发霉。天气还时冷时热,雨水,惊蛰之后就比较多穿短袖了。再过几天,清明之后,就可以将厚衣服收起来了。我看到芒果树最先开花了,有人说蜜蜂不会采芒果花,因为没有亲眼见到蜜蜂在采芒果花,所以也不能确定是不是这样,但是我想中蜂不挑花,像芒果这么美味的水果的花,蜜蜂不会嫌弃吧,我家的蜜蜂采回浅黄色的花粉,不知是不是芒果花粉,找个时间尝一尝这花粉的味道估计就能判断出来了,之前我尝过蜜蜂采回来的红色的花粉,很清香,有很熟悉的味道,果然在附近我就找到了开着红色须球状花朵的合欢树,花朵有点像蒲公英的花,是红色的。花粉的味道有点像含羞草,荆棘花一类树的花粉的味道,很清香。开始我不是很确定红色的花粉就是从合欢树上采的,有一天早晨上班经过树下,看到蜜蜂正在合欢花上翻滚,才证实红色的花粉确实是从合欢树上采的。接着就是广州的市花,红色木棉花盛妆登场了,万绿丛中点缀几棵纯红的木棉树真是好看,上帝的杰作。木棉树开花的时候树干上一片树叶也没有,火红的木棉花,大大的花朵就像一个个红灯笼挂满枝头,仿佛张灯结彩迎接春天的到来。
&感叹完大自然,进入今天的主题,我认识的golang语言。
&在学C语言的时候就有很多疑问,为什么函数只能返回一个结果,如果要返回多个结果怎么办,为什么一定要有类型,类型为什么不能智能判断?无类型,后来学了php后,发现php这类动态语言可以做到无类型了,但是这又带来了性能的问题,因为php内部实现类型的判断,需要经过复杂的计算,损失了一部份性能。所以不是不能实现,而是实现他需要付出代价。关于返回多个结果的疑问,其实那时已经有了解决方案,那就是使函数返回指针,利用数组,返回多个值。虽然不是语言级别的实现,但是解决了函数返回多个值的问题。
&现在go语言在语言级别实现了函数返回多个值的功能,实在太好了。如果golang语言没有去实现这个功能,我想也会有别的语言会实现的,我自已也计划着设计一门语言实现自已想要的功能,包括函数返回多个值的功能,现在golang实现了,其实golang实现了大部份我想要的功能,也是大部份程序员想要的功能吧。
&golang语言自称是一门类似c/C++,java这样的大语言,是系统级基础语言,是编译型语言,是互联网时代的c语言,融合Python等动态语言的开发速度和C/C++等静态编译语言的性能和安全,具备良好的易用性和极佳的执行效率。Google Go语言团队成员在博客中表示,编译完成的Go程序的运行速度接近C语言。Google还表示,Go是一门伟大的系统编程语言,它支持多任务处理,使用全新和轻量级的面向对象设计,另外还支持真闭包(true
closures)和反射(reflection)等非常酷的功能。
&其实Go最主要的特色是多核支持和并行运算,专门针对多处理器系统应用程序的编程进行了优化。
&以下就Golang语言的一些特性写下我的体验
&golang的编译速度还是比较快的,我在写这个blog程序的时候,体验是在go自身的编译程序还没有启动的情况下,第一次编译blog的go程序,会比较慢,大概需要1,2分钟的时间,我用于测试的机器配置是:cpu 双核 2.66GHz ,内存是4G ,64位win7, go 1.4.1
;但是在第二次编译之后,编译速度会比较快,几乎可以达到秒译的速度。第一次会比交慢的原因,可能是因为编译器还没有启动的原因吧,启动编译器需要时间。
&从代码风格方面看,go给我的体验感觉是delphi+php+c的风格 ,可以很明显看到Go语言继承了C的语言代码风格,声明变量和函数需要用到关键字,这不是 c的风格,在学c和delphi的时候给我的体验是,delphi比c罗嗦,var和function是不必要的,以就都不要。 新语言竟捡了回来,这能说是简洁吗?当然golang也有简洁的变量声明方式
s := "字符串";
这种方式有点类似
php等弱类型的语言的风格。所以golang综合了三种编程语言的风格于一身,八面玲珑,讨好各方编程好汉。
关于多返回值,其实多返回值,在我所用过的语言中,都可以轻松实现,但是都不是语言级别的实现,所以GO语言在这方面更贴心一些。简单写一个golang函数多返回值的例子:
package main
import "fmt"
func testFunc(x, y string) (string, string) {
return y, x
func main() {
a, b := testFunc("hello", "world")
fmt.Println(a, b)
返回参数还可以像变量那样命名和使用。如果命名了返回值参数,一个没有参数的 return 语句,会将当前的值作为返回值返回。
package main
import "fmt"
func testFunc(sum int) (x, y int) {
x = sum * 3/9
y = sum - x
func main() {
fmt.Println(testFunc(18))
关于golang函数多返回值的设计,也有一个比较麻烦的地方,当你只需要其中一个值的时候,这时用起来就有点别扭了
返回a,b两个值,但是我只需要a值即可,这时你就需要这样来接收返回值
a,_ := testFunc()
很别扭吧,这个"_"符号看起来很奇怪,如果在C中,可以轻松地这样写
a = testFunc();
这还不算,在if判断中,你不能这样简单地写了
if testFunc()
在Golang中这样写是错误的,因为go编译器不知道你想要比较的是哪一个值
但是在C中这样写就方便多了
if (testFunc())
但是Go语言中也有解决的方法,只是麻烦一些,可以这样写
if a,_=testFunc(),a {
或者分步实现,不能再像C一样的洒脱了,如
a,_=testFunc()
如果让我来改进golang语言函数多返回值接收这个问题,我会给函数多返回值一个默认值
当只有一个变量接收返回值时,默认把第一个返回值赋值给它
这样就可以实现C语言一样的简单调用,不需要奇怪的_符号,也可以使if语句更简洁
&面向对像的主要特点是:封装、继承、多态
那么go语言是怎么展现这些特点的呢
&面向对像 之 封装
&封装是指将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内。该逻辑单元负责将所描述的属性隐藏起来,外界对客体内部属性的所有访问只能通过提供的用户接口实现。这样做既可以实现对客体属性的保护作用,又可以提高软件系统的可维护性。只要用户接口不改变,任何封装体内部的改变都不会对软件系统的其他部分造成影响。 golang 没有明显的类的概念,golang类似于c语言有struct结构体,golang用struct结构体来模拟类和封装。
golang将类和对像的属性封装在struct结构体中,将函数关联到struct结构体上成为该struct结构体的方法。
golang中类与类之间和其他代码之间并没有明显的界限,类的方法可以随意放置在任意位置,类与类之间的方法代码可以交错放置。没有像java中用大括号{} 将类的代码界定在一起。
封装一个很重要的特性是可见性,go语言类的属性和方法的可见性是基于包级别的而不是类级别的,这是跟java等面向对像语言不一样的地方。
golang没有使用关键字来指明可见性,而是通过首字母的大小写来指定可见性。所有的文章都说这样节省了关键字,使用约定的方式来实现,可以看到golang中很多地方使用了约定的方式。
在struct结构体定义中,属性首字母为大字的则为包外可见的属性,在包外可以直接访问,而首字母是小写的则为包内可见,只能在包内访问,包外无法直接访问,只能通过相应的方法来访问。
struct结构体中的方法也是一样的,大写字母开头的方法包外可见,小写开头的方法包外不可见。
golang中类的定义和封装很特别,不像java等面向对像语言
golang中没有明显的class关键字来定义一个类,类的代码也没有明显的界限,没有明显的关键字指定可见性
下面是一个golang定义和封装类的例子:
type Person struct {
这样就声明了一个类,其中可以看到没有public、protected、private的的声明。golang用另外一种做法来实现属性的访问权限:属性的开头字母是大写的则在其它包中可以被访问,否则只能在本包中访问。
以c语言不同,golang扩展了struct结构体,让struct结构体拥有方法
类方法声明
func (this *Person) sing() {
fmt.Println("person sing")
和其它语言不一样,golang声明方法和普通方法一致,只是在func后增加了this *Person这样的声明。
& & & &&面向对像 之 继承
golang 中继承也与java等面向对像语言不同,没有明显的extends关键字指明继承。 也没有明确的继承的概念,go语言是模拟继承,golang使用struct结构体嵌套的方式来模拟实现面向对象的继承特性。
直接上代码举例说明
package main
type Animal struct {
Name string
func (this *Animal) GetName() string {
return this.Name
type Dog struct {
Animal //Animal匿名字段
func main() {
dog := Dog{Animal{"dog", 12}}
fmt.Println(dog.Age)
fmt.Println(dog.GetName())
& & & &&面向对像 之 多态
关于多态,各种理解有很多,基于《On Understanding Types, Data Abstraction, and Polymorphism》中对多态的分类和理解,多态可分为两种:通用的多态和特定的多态。通用的多态又分为参数多态(parametric)和包含多态(inclusion);特定的多态分为重载多态(overloading)和强制多态(coercion)。
强制多态,通常是指类型转换;重载多态,一般是通过方法重载实现多态;参数多态,类似c++中的模板类;包含多态,是我们通常理解的面向对像中通过类继承和接口实现的多态。
我们这里所指的是包含多态,面向对像实现的多态,多态的作用可以实现将接口和实现分离,改善代码的组织结构,增强代码的可读性。在面向对像编程语言中(java,c++),通常是通过运行时绑定(后期绑定,动态绑定)实现。
下面举一下java多态的例子加深对多态的理解 :
* 定义一个基类
public Class Parents {
public void print() {
System.out.println(“parents”);
* 定义两个派生类
public Class Father extends Parents {
public void print() {
System.out.println(“father”);
public Class Mother extends Parents {
public void print() {
System.out.println(“mother”);
* 测试输出结果的类
public Class Test {
public void find(Parents p) {
p.print();
public static void main(String[] args) {
Test t = new Test();
Father f = new Father();
Mother m = new Mother();
t.find(f);
t.find(m);
说说golang如何实现多态,golang没有明显的继承关系,但是golang中提供接口编程,golang的多态主要是通过接口实现的。
直接上代码举例说明:
package main
import "fmt"
type IMessage interface {
type BaseMessage struct {
msg string
func (message *BaseMessage) Print() {
fmt.Println("baseMessage:msg", message.msg)
type SubMessage struct {
BaseMessage
func (message *SubMessage) Print() {
fmt.Println("subMessage:msg", message.msg)
func interface_use(i IMessage) {
func main() {
baseMessage := new(BaseMessage)
baseMessage.msg = "a"
interface_use(baseMessage)
SubMessage := new(SubMessage)
SubMessage.msg = "b"
interface_use(SubMessage)
在java等面向对象语言中普遍存在接口和抽像类,但是在golang中只有接口而没有抽象类
Go语言的主要设计者之一罗布·派克( Rob Pike)曾经说过,如果只能选择一个Go语言的特 性移植到其他语言中,他会选择接口。可见接口在golang中的地位,及其对gloang这门语言所带来的活力。
接口相当于是一份契约,它规定了一个对象所能提供的一组操作。
C++没有提供interface这样的关键字,它通过纯虚基类实现接口,而java则通过interface关键字声明接口。它们有个共同特征就是一个类要实现该接口必须进行显示的声明,如下是java方式:
interface IFoo {
void Bar();
class Foo implements IFoo {
void Bar(){}
这种必须明确声明自己实现了某个接口的方式被称为侵入式接口。
golang采取非侵入式接口的设计理念,在Go语言中,一个类只需要实现了接口规范的所有方法,那么这个类就实现了该接口,并不需要在设计类时声明实现了哪一个接口。
示例代码如下:
type IWriter interface {
Write(buf [] byte) (n int, err error)
type File struct {
func (f *File) Write(buf [] byte) (n int, err error) {
非侵入式接口一个很重要的好处就是去掉了繁杂的继承关系
golang中,有很多与传统面向对象语言不同的地方,其中异常处理就有异于传统面向对象语言
golang中没有了像java 中的异常处理机制,如try catch 这样的异常处理语句
但是golang中仍然有异常处理,golang中提供了两个内置的方法用于处理程序异常:panic和recover
程序中使用panic()抛出异常,使用recover()函数捕获异常信息
golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到 panic(),正常语句就会立即终止,然后执行 defer 语句,再报告异常信息,最后退出 goroutine。如果在 defer 中使用了 recover() 函数,则会捕获错误信息,使该错误信息终止报告。
一定要注意不要滥用 panic-recover,可能会导致性能问题,应当把它作为最后的手段来使用,代码中应当没有,或者很少有panic的东西,一般只在未知输入和不可靠请求时使用.
示例代码如下:
package main
//捕获因未知输入导致的程序异常
func catch(nums ...int) int {
defer func() {
if r := recover(); r != nil {
log.Println("[E]", r)
return nums[1] * nums[2] * nums[3] //index out of range
//主动抛出 panic,不推荐使用,可能会导致性能问题
func toFloat64(num string) (float64, error) {
defer func() {
if r := recover(); r != nil {
log.Println("[W]", r)
if num == "" {
panic("param is null") //主动抛出 panic
return strconv.ParseFloat(num, 10)
func main() {
catch(2, 8)
toFloat64("")
&&多核并行
接口是Go语言编程中数据类型的关键,goroutine和channel是Go并发的两大基石,goroutine相当于一个轻量级的线程(thread),但是又不同于线程(thread),thread由操作系统实现和管理,不同操作系统,对于thread的实现不尽相同;然而goroutine的创建和调度是由Golang运行时进行管理的。thread的启动比process的启动需要更少的资源,但是多个thread之间的上下文切换仍然是需要调度大量的资源(寄存器/Program Count/Stack Pointer/...),Golang有自己的调度器,很多goroutine的数据可以共享,goroutine的启动和goroutine之间的切换更快更省资源,一个Golang程序可以同时管理上千个goroutine。实际上goroutinue机制更像是一个线程池,当一个goroutinue线程被阻塞以后,资源会用来调用另外一个已经准备好的goroutinue线程。
channel,即“管道”,是用来传递数据的一个数据结构,可以向hannel插入数据,也可以从中获取数据。channel本身并没有什么神奇的地方,但是channel加上了goroutine,就形成了一种既简单又强大的请求处理模型,goroutinue和channel一起可以很优雅地做到程度的线程同步,以下是goroutinue和channel的简单示例。
package main
func test(c chan bool, n int) {
for i := 0; i < ; i++ {
fmt.Println(n, x)
func main() {
//设置cpu的核的数量,从而实现高并发
var MULTICORE int = runtime.NumCPU() //获取cpu核数
runtime.GOMAXPROCS(MULTICORE)
c := make(chan bool)
t1 := time.Now().Unix()
for i := 0; i < MULTICORE; i++ {
go test(c, i)
var cnt int = 0
for i := 0; i < MULTICORE; i++ {
temp := <-ch
fmt.Printf("multicore #%d result:%d
", i, temp)
fmt.Println("main ok")
t2 := time.Now().Unix()
fmt.Println("use ", (t2 - t1), " s")
golang中仍然保留有goto语句,但是使用要谨慎。
golang中语句结束不需要分号的设计,使程序员不需要在行末打分号,实际上编译器在编译时自动在行末加上了分号
可以通过以下程序验证:
str := "人生是个圆,有的人走了一辈子也没有走出命运画出的圆圈,"
+"其实,圆上的每一个点都有一条腾飞的切线。"
当需要字符串分行时,就遇到麻烦了
这样的语句,golang编译时自动加上分号后就报错了
golang 会在行末加上分号,那么语句就变成这样:
str := "人生是个圆,有的人走了一辈子也没有走出命运画出的圆圈,";
+"其实,圆上的每一个点都有一条腾飞的切线。";
这个错误,如果第一次遇到,会感到莫名其妙,其实golang支持省略分号,也支持手动打上分号 ,golang是八面玲珑的
关于golang动态库
golang目录不支持生成动态库,仅支持使用其他语言生成的动态库,1.4版 go语言由于需要支持android平台的开发,加入了支持android平台生成动态库。go生成动态库这一特性目前仅仅支持android平台。
需要注意一点golang中函数参数不支持默认值,关于默认值,见仁见智
&&介绍使用go开发的开源项目成功安例:
到现在,Docker几乎是Go再难找到也难以复制的一个成功案例。Docker项目在2014年9月份就拿到了C轮4000万美元融资,版本迭代速度超快,目前从GitHub看到已有78个版本,而它仅仅是再2013年初才正式开始的一个项目而已。目前,国内Docker技术推广也进行的如火如荼,比如 Docker中文社区,CSDN也建立了 Docker专区。CSDN CODE也将在近期与Docker中文社区合作,推出Docker技术文章翻译活动,届时也请大家多多关注,及时关注与参与。
Docker团队之所以喜欢用Go语言,主要是Go具有强大的标准库、全开发环境、跨平台构建的能力。
GitHub托管地址: /docker/docker
2. Kubernetes
Kubernetes是2014年夏天Google推出的Kubernetes,基于Docker,其目的是让用户通过Kubernetes集群来进行云端容器集群的管理,而无需用户进行复杂的设置工作。系统会自动选取合适的工作节点来执行具体的容器集群调度处理工作。其核心概念是Container Pod(容器仓)。
GitHub托管地址: /GoogleCloudPlatform/kubernetes
3. Etcd & Fleet
etcd是由CoreOS开发并维护键值存储系统,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。目前,Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。详情,可了解 《Etcd:用于服务发现的键值存储系统》。 Fleet则是一个分布式的初始化系统。它们之所以选择使用Go语言,则是因为Go语言对跨平台的良好支持,以及其背后的强大社区。
GitHub托管地址: /coreos/etcd
Deis是一个基于Docker和CoreOS的开源PaaS平台,旨在让部属和管理服务器上的应用变得轻松容易。它可以运行在AWS、GCE以及Openstack平台下。详情,可了解 《Deis v1.0正式发布!》。
GitHub托管地址: /deis/deis
Flynn是一个使用Go语言编写的开源PaaS平台,可自动构建部署任何应用到Docker容器集群上运行。Flynn项目受到Y Combinator的支持,目前仍在开发中,被称为是下一代的开源PaaS平台。
GitHub托管地址: /flynn/Flynn
相对上面的几款Go语言在云端和服务器端之外,Lime则显得比较特殊。Lime,则是一款用Go语言写的桌面编辑器程序,被看做是著名编辑器Sublime Text的开源实现。
GitHub托管地址: /limetext/lime
基于Plan 9用Go语言开发的操作系统
Francisco J Ballesteros在9fans邮件列表上宣布了一种新的操作系统Clive。Clive在设计上深受Nix和 Plan 9操作系统的影响,计划运行修改版的nix内核,使用修改版的Go语言开发,其一大特性是zx协议,在概念类似Plan 9的统一资源访问协议。Plan 9的9P协议将所有本地和远程资源以文件形式进行组织
浏览(47229)
同时评论给&
同时评论给原文作者&
(C)2014&aLaxy设置 runtime.GOMAXPROCS(runtime.NumCPU()) 参数后包内变量访问不一致 - Golang中国
12:42 发布 4337 次点击
模块wbw代码:
package wbw
func SrvRun(i int){
sk = "wbw"
fn = fmt.Sprintf("全名: %d", time.Now().UnixNano())
//aeskey = "Aeskey:========================"
aeskey = fmt.Sprintf("========================%d", time.Now().UnixNano())
fmt.Printf("i: %d ,sk: %s, fn: %s, aeskey: %s.\n",i,sk, fn, aeskey)
func SrvDo(c chan bool, i int){
SrvRetRun(i)
func SrvRetRun( i int){
fmt.Printf("i: %d ,sk: %s, fn: %s, aeskey: %s.\n",i,sk, fn, aeskey)
主程序代码:
package main
func main(){
runtime.GOMAXPROCS(runtime.NumCPU())
fmt.Printf("Run: \n")
RunProc := 10
c := make(chan bool, RunProc)
for i :=0; i& RunP i++ {
go wbw.SrvDo(c, i)
for i := 0; i & RunP i++ {
fmt.Println("DONE.")
程序运行结果:
i: 2 ,sk: wbw, fn: 全名: 3181325, aeskey: ========================3212440.
i: 2 ,sk: wbw, fn: 全名: 3181325, aeskey: ========================3212440.
i: 3 ,sk: wbw, fn: 全名: 3278175, aeskey: ========================3283101.
i: 3 ,sk: wbw, fn: 全名: 3278175, aeskey: ========================3283101.
i: 4 ,sk: wbw, fn: 全名: 3349461, aeskey: ========================3355269.
i: 4 ,sk: wbw, fn: 全名: 3349461, aeskey: ========================3302891.
i: 5 ,sk: wbw, fn: 全名: 3382401, aeskey: ========================3386580.
i: 5 ,sk: wbw, fn: 全名: 3382401, aeskey: ========================3386580.
i: 0 ,sk: wbw, fn: 全名: 3349461, aeskey: ========================3302891.
i: 6 ,sk: wbw, fn: 全名: 3410279, aeskey: ========================3414368.
i: 0 ,sk: wbw, fn: 全名: 3410279, aeskey: ========================3414368.
i: 6 ,sk: wbw, fn: 全名: 3410279, aeskey: ========================3414368.
i: 1 ,sk: wbw, fn: 全名: 3432720, aeskey: ========================3436969.
i: 7 ,sk: wbw, fn: 全名: 3438829, aeskey: ========================3442387.
i: 1 ,sk: wbw, fn: 全名: 3438829, aeskey: ========================3442387.
i: 7 ,sk: wbw, fn: 全名: 3438829, aeskey: ========================3442387.
i: 8 ,sk: wbw, fn: 全名: 3461325, aeskey: ========================3465624.
i: 8 ,sk: wbw, fn: 全名: 3470581, aeskey: ========================3474104.
i: 9 ,sk: wbw, fn: 全名: 3470581, aeskey: ========================3474104.
i: 9 ,sk: wbw, fn: 全名: 3470581, aeskey: ========================3474104.
模块wbw的两个函数: SrvRun, SrvRetRun 他们的输出应该是一致的才对,可是从输出来看 i: 4这两行的内容并不一致,输出的不一致是由于变量 fn或者aeskey被修改了。
如果我将主程序中语句runtime.GOMAXPROCS(runtime.NumCPU()) 注释后得到的结果始终都是正确的,
请问我如果要启用GOMAXPROCS特性,应该如何才能确保我模块 SrvRun, SrvRetRun两个的函数输出是一致的,而且我发现如果开启GOMAXPROCS特性,不同GO程中的相同的变量名会发生改变。
请问我应该如何处理该问题。谢谢!
wzhuzhu 于
12:58 修改
是否是main在import wbw模块的时候,在 go wbw.SrvDo(c, i) 使用的是继承自main中导入的wbw模块的变量,导致了不同的GO程都访问了同一个变量,才会出现上面运行程序的这种情况?
通道设置了缓存,就编程非阻塞的了,除非缓存满了,所以变量被其他通道给重置了.
管道没有缓存好像也不行,因为阻塞前变量就被重置了,可行的方法有几种:
1,把变量设置为数组或切片,通过下表i来赋值.
2,变量通过参数传入,而不是在该包内设置为全局的.
3,在go后立即调用&-c,不过这样就不是多进程了
多谢,包内不使用全局的变量应该简单一点。
多线程访问了共享的全局变量,访问前应该加锁…
楼上正解, 并发访问, 应该加锁
后方可回复, 如果你还没有账号你可以
一个帐号。}

我要回帖

更多关于 golang 应用场景 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信