热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Go编程语言评估报告

Go编程语言评估报告1.Go简介Go是由Google于2007年9月21日开始开发,2009年11月10日开放源码,2012年3月28日推出第一个正式版

 


Go编程语言评估报告

 

1. Go简介

 

Go是由Google于2007年9月21日开始开发,2009年11月10日开放源码,2012年

328日推出第一个正式版本的通用型编程语言。它为系统编程而7设计,是强类型化的

语言,具有垃圾回收机制,并显式支持并发编程。Go程序由包构造,以此来提供高效的

依赖管理功能。当前的编译器实现使用传统的编译-链接模型来生成可执行的二进制文

件。


十年以来,主流的系统级编程语言并未出现过,但在这期间,计算环境已经发生了巨大的

变化。以下是一些变化趋势:



相计算机的速度变得极快,但软件开发还不够快。

在今天,依赖管理已然成为了软件开发中当重要的部分,但传统的C家族语言以头文

的方式组织源码,这与清晰的依赖分析以及快速编译背道而驰。

Java和C++等语言的类型系统比较笨重,人们的反抗越来越多,因此他们转向了

PythonJavascript之类的动态类型语言。

目前流行的系统语言对于像垃圾回收及并行计算等基本思想的支持并不算好。

多核计算机的出现产生了一些麻烦与混乱。

随着系统的庞大,分布式的要求越来越高,现有的语言开发分布式系统越来越笨重以及难以维护。


 

Go则是一种新的语言,一种并发的、带垃圾回收的、快速编译的语言。它满足了以下特点:







它可以在一台计算机上用几秒钟的时间编译一个大型的Go程序。

Go为软件构造提供了一种模型,它使依赖分析更加容易,且避免了大部分C风格

include文件与库的开头。

Go是静态类型的语言,它的类型系统没有层级。因此用户不需要在定义类型间的关系上

花费时间,这样感觉起来比典型的面向对象语言更轻量级。

Go完全是垃圾回收型的语言,并为并发执行与通信提供了基本的支持。

按照其设计,Go打算为多核机器上系统软件的构造提供一种方法。




 




Go试图成为结合解释型编程的轻松、动态类型语言的高效以及静态类型语言的安全的编译型语

言。它也打算成为现代的,支持网络与多核计算的语言。要满足这些目标,需要解决一些语言上

的问题:一个富有表达能力但轻量级的类型系统,并发与垃圾回收机制,严格的依赖规范等等。

这些无法通过库或工具解决好,因此Go也就应运而生了。




 




2. C/C++的缺陷

 

a. 全局变量的初始化顺序

由于在C/C++中,全局变量的初始化顺序并不确定,因此依赖于全局变量初始化

顺序的操作,可能会给程序带来不可预知的问题。

 

b. 变量默认不会被初始化

由于变量默认不会被初始化,因此如果在程序中忘记初始化某个变量,就有可能

造成一些奇怪的细节性错误,以至于在Coding Standard中都为之专门加以强调。

 

c. 字符集的支持1

C/C++最先支持的字符集是ANSI虽然在C99/C++98之后提供了对Unicode的支

持,但在日常的编码工作中却要在ANSIUnicode之间来回转换,相当地繁琐。

 

d. 复杂的继承模式

C++提供了单/多继承,而多继承则引入了大量的复杂性,比如钻石型继承等。

细节请参阅《深度探索C++对象模型》。

 

e. 对并发的支持

在C++中,并发更多的是通过创建线程来启用,而线程间的通信则是通过加锁共

享变量来实现,很容易死锁。虽然在C++11中添加了并发处理机制,但这给本来就十分复

杂的类型系统又添加了更重的负担。

 

f. 不支持自动垃圾回收

关于这一点存在争议。内存泄漏是C/C++程序员经常遭遇到的问题,但随着垃圾

回收算法的成熟,对于大多数开发者来说,自动回收带来的便利已经超过手工操作提高的

效率。而在C++中虽然可使用智能指针来减少原生指针的使用,但不能杜绝它,因此这个

问题仍然存在。

 

g. 落后的包管理机制

C/C++中采用.h/.c(pp)文件来组织代码,这种方式使编译时间变得过于漫长

;C++中的模板更让这个问题雪上加霜。

 

h. C++编译器总会私自生成一些代码

比如: 构造函数/析构函数/new/delete等。如果是动态库,当include不同版本的头

文件时,容易生成版本不兼容的代码。

 

i. 不加约束的指针使用是导致C/C++软件BUG的重要根源之一

3. Go的优势


1


C/C++编译时的编码方式也不确定。main.cpp如果是UTF-8编码, 在gcc和VC下的行为也


会不一样。——柴树杉




正如语言的设计者之一Rob Pike所说:

我们——Ken,Robert和我自己曾经是C++程序员,我们设计新的语言是为了解

决那些我们在编写软件时遇到的问题。

 

这些问题中的大部分,就是在第2节中列举的内容。这一小节就是Go针对这些缺陷提出的

解决方案。

 

a. Init

每个包都可以定义一个或多个init函数2(原型为 func init()),init函数在包初次

被导入时调用,同一个包内的多个init函数的执行的顺序是不定的,而如果这个包又导入

了其他的包,则级连调用,所有包import完成,所有init函数执行完后,则开始main的执

行。

而对于全局变量,以一个简单的例子来说明:

 

// package p

var gInt int

 

// package a

import "p"

 

// package b

import "p"

 

// package main

import (

"a"

"b"

)

 

在package p中,我们定义了一个全局变量gInt,而p被package a,b所import,接

着package main又按序importa,b,即ab前被importaimportp,所以此时gInt

初始化,这样就解决了C/C++中全局变量初始化顺序不一致的问题。

 

b. 默认自动初始化

Go引入了零值的概念,即每个对象被创建的时候,默认初始化为它相应类型的零

值。例如,string””,指针为nil,int0等等,这样就保证了变量在使用时,不会因为忘

记初始化而出现一些莫名其妙的问题。此外,由于零值的引入,也方便了代码的编写。比

如说sync包的mutex类型,在引入零值后,就能以如下方式使用:




2


每个源文件也可包含多个init函数。多个init之间的调用顺序不确定。init函数本身不能被其它变量或函数引


用(调用或取函数地址)。——柴树杉



3


var locker sync.Mutex

locker.Lock()

defer locker.Unlock()


而相应的C/C++代码,可能就要这样写了:

 

CRITICAL_SECTION locker

InitializeCriticalSection(&locker)

EnterCriticalSection(&locker)

LeaveCriticalSection(&locker)

DeleteCriticalSection(&locker)

 

忘记任何一步操作,都将造成死锁(dead lock)或者其他的问题。

 

c. UTF-8

Go语言原生支持UTF-8编码格式3。同时Go涉及到字符串的各种包,也直接为

UTF-8提供了支持,比如:

str := "示例"

if str == "示例" {...}

 

d. 只支持组合不支持继承

OOPGo中是通过组合而非继承来实现的,因为继承存在一些弊端,比如

:不适应变化,会继承到不适用的功能。所以在编码实践中一般建议优先使用组合而

非继承。在Go中则更进一步,直接去掉了继承,只支持组合。在定义struct时,采用匿名

组合的方式,也更好地实现了C++中的实现继承,而在定义interface时,也可以实现接

口继承。比如:

type A struct{}

func (a A) HelloA() {

}

 

type B struct{}

func (b B) HelloB() {

}

 

type C struct {

A

B

}

 

c := &C{}

c.HelloA()

c.HelloB()

 

此时c就拥有了HelloAHelloB两个方法,即我们很容易地实现了实现继承

 

 

同时已经支持带BOMUTF-8了。——柴树杉




 

e. Go程(goroutine)与信道(channel)

Go对并发的支持,采用的是CSP模型,即在代码编写的时候遵循通过通信来共

享内存,而非通过共享内存来通信的原则。为此,Go提供了一种名为“Go的抽象。由

Go程是一种高于线程的抽象,因此它使用起来也就更加轻量方便。而当多个Go程需要

通信的时候,信道就成为了它们之间的桥梁。例如:

 

func goroutine(pass chan bool) {

fmt.Println("hello, i'm in the goroutine")

pass <- true

}

 

func main() {

pass :&#61; make(chan bool)

go goroutine(pass)

<-pass

fmt.Println("passed")

}

 

代码中通过关键字chan来声明一个信道&#xff0c;在函数前加上关键字go来开启一个新的

Go程。此Go程在执行完成后&#xff0c;会自动销毁。而在通信过程中&#xff0c;可通过<-操作符向信道中

放入或从中取出数据。

 

f. 自动垃圾回收

C#Java等语言类似&#xff0c;为了将程序员从内存泄漏的泥沼中解救出来&#xff0c;Go提供

了自动垃圾回收机制&#xff0c;同时不再区分对象是来自于栈(stack)还是堆(heap)

 

g. 接口(interface)

除Go程外&#xff0c;Go语言的最大特色就是接口的设计&#xff0c;Go的接口与Java的接口&#xff0c;

C&#43;&#43;的虚基类是不同的&#xff0c;它是非侵入式的&#xff0c;即我们在定义一个struct的时候&#xff0c;不需要显

式的说明它实现了哪一/几个interface&#xff0c;而只要某个struct定义了某个interface所声明的

所有方法&#xff0c;则它就隐式的实现了那个interface&#xff0c;即所谓的Structural-Typing&#xff08;关于Duck-

TypingStructural-Typing的区别&#xff0c;请参考minux.ma的相关注释&#xff09;。

 

假设我要定义一个叫Shapeinterface&#xff0c;它有CircleSquareTriangle等实现

类。

在java等语言中&#xff0c;我们是先在大脑中从多个实现中抽象出一个interface&#xff0c;即:

在定义Shape的时候&#xff0c;我们会先从实现类中得出共性。比如它们都可以计算面

积&#xff0c;都可以被绘制出来&#xff0c;即Shape拥有AreaShow方法。在定义出了Shape过后&#xff0c;再定

CircleSquareTriangle等实现类&#xff0c;这些类都显式的从Shape派生&#xff0c;即我们先实现了

接口再实现了实现。在实现实现的过程中&#xff0c;如果发现定义的接口不合适&#xff0c;因为实现

式地指定了它派生自哪个基类&#xff0c;所以此时我们需要重构&#xff1a;




 

public interface Shape {

public float Area();

public void Show();

}

public class Circle : implements Shape {

public float Area() { return …}

public void Show() {…}

}

// (同理SquareTriangle)

 

而在Go中&#xff0c;由于interface是隐式的&#xff0c;非侵入式的&#xff0c;我们就可以先实现Circle

SquareTriangle等子类。在实现这些实现类的过程中&#xff0c;由于知识的增加&#xff0c;我们可以更

好地了解哪些方法应该放到interface中&#xff0c;即在抽象的过程中完成了重构。

 

type Circle struct {}

func (c Circle) Area() float32 {}

func (c Circle) Show() {}

// (同理SquareTriangle)

type Shape interface {

Area() float32

Show()

}

 

这样CircleSquareTriangle就实现了Shape

 

对于一个模块来说&#xff0c;只有模块的使用者才能最清楚地知道&#xff0c;它需要使用由其它被

使用模块提供的哪些方法&#xff0c;即interface应该由使用者定义。而被使用者在实现时&#xff0c;并不知

道它会被哪些模块使用&#xff0c;所以它只需要实现自己就好了&#xff0c;不需要去关心接口的粒度是多细

才合适这一类的琐碎问题。interface是由使用方按需定义&#xff0c;而不用事前规划。

 

Go的interface与Java等的interface相比优势在于&#xff1a;

1.    按需定义&#xff0c;最小化重构的代价。

2.    先实现后抽象&#xff0c;搭配结构嵌入&#xff0c;在编写大型软件的时候&#xff0c;我们的模块可

以组织得耦合度更低。

 

h. Go命令

在Unix/Linux下为了编译程序的方便&#xff0c;都可能需要编写makefile或者各种高级的自

动构建工具&#xff08;Windows也存在类似的工具&#xff0c;只不过被各种强大的IDE给隐藏在背后了&#xff09;。

而Rob Pike等人当初发明Go的动机之一就是&#xff1a;“Google的大型的C&#43;&#43;程序的编译时间过

。所以为了达到&#xff1a;编译Go程序时&#xff0c;作为程序员除开编写代码外&#xff0c;不需要编写任何配

置文件或类似额外的东西。这个目标&#xff0c;引入了Go命令族。通过Go命令族&#xff0c;你可以很容

易从实现的在线repository上获得开源代码&#xff0c;编译并执行代码&#xff0c;测试代码等功能。这与C/

C&#43;&#43;的处理方式相比&#xff0c;前进了一大步。

 

i. 自动类型推导




Go虽然是一门编译型语言&#xff0c;但是在编写代码的时候&#xff0c;却可以给你提供动态语言的

灵活性。在定义一个变量的时候&#xff0c;你可以省略类型&#xff0c;而让编译器自动为之推导类型&#xff0c;这样

减少了程序员的输入字数。比如:

i :&#61; 0  var i int

s :&#61; "hello world"  var s string &#61; "hello world"

 

j. 强制编码风格规范

在C/C&#43;&#43;中&#xff0c;大家为大括号的位置采用K&R还是ANSI&#xff0c;是使用tab还是

whitespace&#xff0c;whitespace2个字符还是4个字符等琐碎的问题而争论不休。每个公司内

部都定义了自己的Coding Standard来强制约束。而随着互联网的蓬勃发展&#xff0c;开源项目的

越发增多&#xff0c;这些小问题却影响了大家的工作效率。而有一条编程准则是“less is more”。为

了一致性&#xff0c;Go提供了专门的格式化命令go fmt&#xff0c;用以统一大家的编码风格。

 

作为程序员&#xff0c;你在编写代码的时候&#xff0c;可以按你喜欢的风格编写。编写完成后&#xff0c;

执行一下go fmt命令&#xff0c;就可以将你的代码统一成Go的标准风格。这样你在接触到陌生的

Go代码时&#xff0c;减少了因为编码风格差异带来的陌生感&#xff0c;强调了一致性。

 

k. 自带单元测试及性能测试工具

C/C&#43;&#43;虽未提供官方的单元测试与性能测试工具&#xff0c;但有大量第三方的相关工具。

而由于每个人接触的&#xff0c;喜欢的工具可能不一样&#xff0c;就造成了在交流时的负担。有鉴于此&#xff0c;

Go提供了官方测试工具go test&#xff0c;你可以很方便地编写出单元测试用例。比如这样就完成

了一个单元测试的编写&#xff1a;

package test

 

// example.go

func Add(a, b int) int {

return a &#43; b

}

// example_test.go

func TestAdd(t *testing.T) {

//定义一个表格&#xff0c;以展示 table-driven 测试

table :&#61; []struct {

a, b, result int

}{

{1, 0, 1},

{1, 2, 3},

{-1, -2, 0},

}

 

for _, row :&#61; range table {

if row.result !&#61; Add(row.a, row.b) {

t.Fatalf("failed")

}

}

}

 

同理性能测试。

编写完成后执行go test就可完成测试。




 




l. 云平台的支持

最近几年云计算发展得如火如荼&#xff0c;Go被称为“21世纪的C语言&#xff0c;当然它也不能忽

视这一块的需求。现在有大量的云计算平台支持Go语言开发&#xff0c;比如由官方维护的GAE&#xff0c;

第三方的AWS等。

 

m. 简化的指针

这一条&#xff0c;可能不算优势&#xff0c;在C/C&#43;&#43;中指针运算的功能非常强大&#xff0c;但是带来的危害也很

突出&#xff0c;所以在Go中指针取消了运算功能&#xff0c;只保留了引用/解引用功能 

 

n. 简单的语法&#xff0c;入门快速&#xff0c;对于新成员很容易上手

Go本质上是一个C家族的语言&#xff0c;所以如果有C家族语言的经验&#xff0c;很容易上手。

 

4. Go的劣势4

 

a. 调度器的不完善

 

b. 原生库太少/

 

c. 32bit上的-内存泄漏

 

关于这一点&#xff0c;Go的贡献者minux.ma在Golang-China讨论组上有详细解释:

 

目前Go使用的GC是个保守的GC&#xff0c;换句通俗的话说就是宁可少释放垃圾&#xff0c;也不

可误释放还在用的内存&#xff1b;这一点反映在设计上就是会从堆栈、全局变量开始&#xff0c;把所有可能

是指针的uintptr全部当作指针&#xff0c;遍历&#xff0c;找到所有还能访问到的内存中的对象&#xff0c;然后把剩下

的释放。

 

那么如何判断一个uintptr可能是指针呢&#xff1f;大家知道Go的内存分配是参考的

tcmalloc&#xff0c;并做了一些改动。原先tcmalloc是使用类似页表的树形结构保存已经从操作

系统中获得的内存页面&#xff0c;Go使用了另外一个办法。由于Go需要维护每个内存字的一些

状态&#xff08;比如是否包含指针&#xff1f;是否有finalizer&#xff1f;是否是结构体的开始&#xff1f;还有上面提到的是

否还能访问到的状态&#xff09;&#xff0c;综合在一起是每个字需要4bit信息&#xff1b;于是Go就先找一片区域

&#xff08;arena&#xff09;&#xff0c;以不可访问的权限从操作系统那里申请过来&#xff08;mmap

的prot参数是PROT_NONE&#xff09;&#xff0c;然后根据每一个uintptr对应4位申请一片RW的内

存&#xff08;bitmap&#xff09;与前面的arena对应&#xff1b;这样已知heap上内存的地址想获得对应的bitmap地址

就很简单了&#xff0c;不需要像tcmalloc似的查找&#xff0c;直接简单的右移和加法就能获得&#xff1b;同时呢&#xff0c;操




 




4


可以归纳为: 性能/生态/Bug/工具等。——柴树杉




作系统的demand paging会自动处理还没有使用到的bitmap

这里大家就明白了为啥Go用了那么大的虚拟内存&#xff08;arena&#xff09;并且知道为啥经常在

内存不足的时候panic说申请到的内存不在范围了&#xff08;因为内存不在bitmap所能映射的范围

里&#xff0c;当然多个bitmap是可以解决这个问题的&#xff0c;不过目前还不支持&#xff09;&#xff1b;回到开始的那个问

题&#xff0c;既然arena有个地址范围&#xff0c;判断一个uintptr是否可能是指针就是判断是否在这个范围

里了。

 

这样的问题就来了。如果我有一个int32&#xff0c;他的内容恰巧在那个范围里&#xff0c;更碰巧的

是如果把它当作指针&#xff0c;它恰巧指向一个大的数据结构&#xff0c;那么GC只能认为那个数据结构还

在使用中。这样就造成了泄露。这个问题在32/64位平台上都是存在的。但是在32位上

问题更严重些&#xff0c;主要是32位表示的地址空间有768MB是Arena&#xff0c;也就是说一个均匀分布的

uintptr是指针的概率是768/4096&#xff0c;这个远比64位系统的16GiB/(2^64B)的概率大得多。

 

Go 1.1不出意外的话会使用记录每个heap上分配的对象的类型的方式来几乎完整

地解决这个问题&#xff1b;说几乎完整是因为&#xff0c;堆栈上的数据还是没有类型的&#xff0c;所以这里面还是有

前面说的问题的&#xff0c;只不过会影响会小很多了。

 

d. 无强大IDE支持

 

e. 最大可用内存16G限制

 

因为今年3月28日Go才推出Go 1&#xff0c;所以目前Go还存在不足。ace这几个缺陷在

2013年初的Go 1.1中会得到解决&#xff0c;而bd则需要等时间的积累才能完善。

 

5. Go的争议

 

a. 错误处理机制

在错误处理上&#xff0c;Go不像C&#43;&#43;Java等提供了异常机制&#xff0c;而是采取检查返回值的方

案&#xff0c;这是目前Go最大争议所在。

 

反对者的理由&#xff1a;

1.    每一步&#xff0c;都得做检查繁琐&#xff0c;原始。

2.    返回的error类型可以通过 _ 给忽略掉。

3.    返回的官方error接口只有一个Error()方法来输出字符串&#xff0c;无法用来判断

复杂的错误&#xff0c;比我定义一个方法OpenJsonFile(name string) (jFile JFile,

err Error), 这个方法可能引法的错误有两种1.文件没找到&#xff0c;2,文件解析

错误&#xff0c;这时我希望返回的错误中带有两个信息&#xff0c;1&#xff0c;错误码&#xff0c;2错误的提

示&#xff0c; 错误码&#xff0c;用于程序中的判断&#xff0c;错误提示用于快速了解这个错误&#xff0c;现

在官方的error接口只有错误提示&#xff0c;而没有错误码。

 

支持的理由&#xff1a;

1.    在离错误发生最近的地方&#xff0c;可以用最佳的方式处理错误。

2.    异常在crash后抛出的stack信息&#xff0c;对于别有用心者&#xff0c;会泄漏关键信息&#xff1b;而

对于最终用户&#xff0c;他将看不明白究竟发生了什么情况。而使用错误机制能

让你有机会将stack信息替换为更有意义的信息&#xff0c;这样就能提高安全性和

用户友好性。




3.    异常也可以默认处理。

 

b. new与变量初始化

Go中&#xff0c;newdelete和在C&#43;&#43;中的含义是不一样的。delete用以删除一个

map项&#xff0c;而new用以获得一个指向某种类型对象的指针&#xff0c;而因为Go支持类似如下的语法

&#xff1a;

 

type T struct {

}

obj :&#61; &T{}  obj &#61; new(T)

 

同时Go提供另一个关键字make用以创建内建的对象&#xff0c;所以&T{}这种语法与

make合起来&#xff0c;就基本可以替代new&#xff08;但目前new(int)这类基本类型指针的创建&#xff0c;则无法用

&T{}的写法&#xff09;&#xff0c;因此new看起来有点冗余了&#xff0c;这与Go的简单原则有点不一致。

 

c. For…range不能用于自定义类型

为了遍历的方便&#xff0c;Go提供了for-range语法&#xff0c;但是这种构造只能用于built-in类型&#xff0c;

slicemapchan&#xff1b;而对于非built-in类型&#xff0c;即使官方包container中的相关数据结构也

不行&#xff0c;这降低了for-range的易用性。而目前在不支持泛型的前提下&#xff0c;要实现一个很友好的

for-range看起来还是很不容易的。

 

d. 不支持动态链接

目前Go只支持静态链接&#xff08;但gccgo支持动态链接&#xff0c;Go 1.1可能会支持部分动态链

接&#xff09;&#xff0c;这又是另一个引起争论的地方。争论双方的论据就是动态链接/静态链接的优、缺

点&#xff0c;在此不再赘述。

 

e. 无泛型

现代的大多数编程语言都提供了对泛型的支持&#xff0c;而在Go 1中则没有提供对泛型

的支持。按官方团队成员Russ Cox的说法&#xff0c;支持泛型要么降低编译效率&#xff0c;要么降低程序

员效率&#xff0c;要么降低运行效率。而这三个恰好与Go的快速、高效、易编写的目标是相冲突

的。同时Go提供的interface{}可以降低对泛型的期望和需求&#xff0c;因此是否需要泛型也成了争

论的焦点。

 

f. 首字母大写表示可见性

Go中只支持包级别的可见性&#xff0c;即无论变量、结构、方法、还是函数等&#xff0c;如果以大

写字母开头&#xff0c;则它的可见性是公共的&#xff0c;在其它包中可加以引用&#xff1b;如果以小写字母开头&#xff0c;

则其可见性为其所在的包。由于Go支持UTF-8&#xff0c;而对于像中文这种没有大小写分别的字

符在需要导出时&#xff0c;就会出现问题。关于这个问题&#xff0c;支持者的理由是&#xff1a;既然语言本身支持

UTF-8&#xff0c;那么在变量命名上就应该是一致的&#xff1b;不支持者的理由是&#xff0c;中国人用中文命名&#xff0c;日

本人用日语命名而且非要用类似中文这类符号编写的话&#xff0c;可以在中文符号前加一个英文

符号.比如:

 

var 不可导出 int &#61; 0

var E可导出 int &#61; 0




 




6. 替代方案

 

a. Cgo

在前边的劣势部分有讲过&#xff0c;Go缺乏原生包&#xff0c;而现在世面上已经有大量的C实现的

高质量的第三方库&#xff0c;比如OpenALOpenCLOpenGL等。为了解决这个问题&#xff0c;Go引入

一个叫做cgo的命令&#xff0c;通过遵守简单的约定&#xff0c;就可以将一个C库wrapper成一个Go包&#xff0c;这

也是为何在短短几年Go拥有了大量高质量包的原因。cgo相关示例在此不再展示。

 

b. B/S

因为到目前为止&#xff0c;Go尚未提供GUI相关的支持。同时在云计算时代&#xff0c;越来越多的

程序采用了B/S结构&#xff0c;而Go对Web编程提供了最完善的支持&#xff0c;所以如果程序需要提供界面

&#xff0c;无论是本地程序&#xff0c;还是服务器程序&#xff0c;在当下建议使用B/S架构来替代。

 

7. 参考资料及推荐

 

a. 本文内容多来自于以下三个Google Groups:

1.    Golang-Dev&#xff08;Go语言开发讨论组&#xff09;

2.            Golang-Nuts&#xff08;Go语言使用讨论组&#xff09;

3.            Golang-China&#xff08;Go语言使用中文讨论组&#xff09;

 

b. 推荐书籍

1.    《学习Go语言》

Mikespook翻译的一本英文学习资料。

2.    Go语言编程》

由许式伟主笔的国内第2本关于Go语言的中文书籍。

3.    Go Web编程》

一本由Asta谢主笔&#xff0c;我参与审阅的开源中文书籍。如果在阅读过程中有

任何疑问&#xff0c;请加入QQ群&#xff1a;259316004讨论。

 

c. 推荐框架

1.    Golanger Web框架

一个由leetaifookborderj开发的开源Web框架。如果您在阅读/使用过程

中有任何疑问&#xff0c;请加入QQ群&#xff1a;29994666讨论。

 

d. 推荐站点




 




1.


Golang-Chinese




 




一个主要由我维护的Google&#43; Page&#xff0c;包含一些我认为值得分享的有关

Go语言的资料。如果哪位愿意共同维护&#xff0c;请留言告之&#xff0c;Thanks

2.            Go-zh

Go语言官方网站的中文翻译版&#xff0c;除pkg文档外&#xff0c;其它文档基本翻译完

成。如果哪位愿意共同翻译维护&#xff0c;请联系Oling Cat




 




注&#xff1a;




除特别声明外&#xff0c;本文档内容使用CC BY-SA 3.0 License&#xff08;创作共用 署名-相同方式共享

3.0许可协议&#xff09;授权&#xff0c;代码遵循BSD 3-Clause License&#xff08;3项条款的BSD许可协议&#xff09;。



BSD 3-CLAUSE LINCENSE

 

Copyright (c) 2012, LewGun and The Contributors All rights reserved.

Redistribution and use in source and binary forms, with or without

modification, are permitted provided that the following conditions are met:

      Redistributions of source code must retain the above copyright notice,

this list of conditions and the following disclaimer.

      Redistributions in binary form must reproduce the above copyright notice,

this list of conditions and the following disclaimer in the documentation

and/or other materials provided with the distribution.

      Neither the name of the LewGun nor the names of its contributors may be

used to endorse or promote products derived from this software without

specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"

AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS

BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

POSSIBILITY OF SUCH DAMAGE.


网址&#xff1a;http://www.qukuailianxueyuan.io/



欲领取造币技术与全套虚拟机资料

区块链技术交流QQ群&#xff1a;756146052  备注&#xff1a;CSDN

尹成学院微信&#xff1a;备注&#xff1a;CSDN






推荐阅读
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • Python操作MySQL(pymysql模块)详解及示例代码
    本文介绍了使用Python操作MySQL数据库的方法,详细讲解了pymysql模块的安装和连接MySQL数据库的步骤,并提供了示例代码。内容涵盖了创建表、插入数据、查询数据等操作,帮助读者快速掌握Python操作MySQL的技巧。 ... [详细]
author-avatar
yun建2502930453
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有