来谈谈Golang吗?Golang真的不行吗?Golang的高并发平时真能用到吗?生态不好,不方便写业务。岗位很少

互联网技术日新月异,当时的服务器软件系统规模庞大,程序都是由众多程序员共同创作,源代码也以数百万行计,而且实际上还需要每天都进行更新。

我们来从官方go起源来寻找问题:

AtthetimeofGo'sinception,onlyadecadeago,theprogrammingworldwasdifferentfromtoday.ProductionsoftwarewasusuallywritteninC++orJava,GitHubdidnotexist,mostcomputerswerenotyetmultiprocessors,andotherthanVisualStudioandEclipsetherewerefewIDEsorotherhigh-leveltoolsavailableatall,letaloneforfreeontheInternet.

译文:在Go诞生之时,也就是十年前,编程世界与今天不同。生产软件通常是用C++或Java编写的,GitHub不存在,大多数计算机还不是多处理器,除了VisualStudio和Eclipse之外,很少有IDE或其他高级工具可用,更不用说在互联网上免费了。

Meanwhile,wehadbecomefrustratedbytheunduecomplexityrequiredtousethelanguagesweworkedwithtodevelopserversoftware.ComputershadbecomeenormouslyquickersincelanguagessuchasC,C++andJavawerefirstdevelopedbuttheactofprogramminghadnotitselfadvancednearlyasmuch.Also,itwasclearthatmultiprocessorswerebecominguniversalbutmostlanguagesofferedlittlehelptoprogramthemefficientlyandsafely.

译文:与此同时,我们对使用我们所使用的语言来开发服务器软件所需的过度复杂性感到沮丧。自从C、C++和Java等语言首次开发以来,计算机的速度已经变得非常快,但编程行为本身却没有进步那么多。此外,很明显,多处理器正在变得普遍,但大多数语言对高效、安全地对其进行编程几乎没有提供帮助。

说是讨论,倒不如说是吐槽更好,他们一致认为:与其在臃肿的语言上不断增加新的特性,不如简化编程语言,其他的编程语言老前辈,都是有严重的历史包袱,于是Go语言就是在这样的环境下出现的,Go语言为了解决高难度、低效率、高耗资源的系统编程问题。

Wedecidedtotakeastepbackandthinkaboutwhatmajorissuesweregoingtodominatesoftwareengineeringintheyearsaheadastechnologydeveloped,andhowanewlanguagemighthelpaddressthem.Forinstance,theriseofmulticoreCPUsarguedthatalanguageshouldprovidefirst-classsupportforsomesortofconcurrencyorparallelism.Andtomakeresourcemanagementtractableinalargeconcurrentprogram,garbagecollection,oratleastsomesortofsafeautomaticmemorymanagementwasrequired.

译文:我们决定退后一步,思考随着技术的发展,未来几年哪些主要问题将主导软件工程,以及新语言如何帮助解决这些问题。例如,多核CPU的兴起表明,语言应该为某种并发或并行性提供一流的支持。为了使大型并发程序中的资源管理易于处理,需要垃圾收集,或者至少需要某种安全的自动内存管理。

最初的讨论是在2007年9月20日星期四下午进行的。第二天下午2点,RobertGriesemer、RobPike和KenThompson在Google山上43号楼的雅温得会议室举行了有组织的会议查看校园。该语言的名称于25日出现,第一个邮件线程中出现了几条关于该设计的消息:

由罗布在2007年9月25号回复给肯.罗伯特的有关新的编程语言讨论主题的邮件。

Subject:Re:proglangdiscussion

From:Rob'Commander'Pike

Date:Tue,Sep25,2007at3:12PM

To:RobertGriesemer,KenThompson

ihadacoupleofthoughtsonthedrivehome.

'go'.youcaninventreasonsforthisnamebutithasniceproperties.

it'sshort,easytotype.tools:goc,gol,goa.ifthere'saninteractive

debugger/interpreteritcouldjustbecalled'go'.thesuffixis.go

邮件正文大意为:在开车回家的路上我得到了些灵感,给这门编程语言取名为“go”,它很简短,易书写。工具类可以命名为:goc、gol、goa。交互式的调试工具也可以直接命名为“go”,语言文件后缀名为.go等等。

该语言是Go还是Golang?

该语言称为Go。“golang”这个绰号的出现是因为该网站最初是golang.org。(当时还没有.dev域。)不过,许多人使用golang名称,并且它作为标签很方便。例如,该语言的Twitter标签是“#golang”。无论如何,该语言的名称就是简单的Go。

GoatGoogle:LanguageDesignintheServiceofSoftwareEngineering

译:为软件工程服务的语言设计

这是由罗布·派克阐述的,在当时Go的设计就是为了Google公司的软件工程问题。

workingwithGoisintendedtobefast:itshouldtakeatmostafewsecondstobuildalargeexecutableonasinglecomputer.

译:使用Go的目的是快速:它应该最多只需几秒钟即可在单台计算机上生成大型可执行文件。

软件工程指导了Go的设计:构建一种轻量级且令人愉悦的高效编译编程语言。

Go语言从开源到现在,已经有多年了,虽然Go语言作为一个标准的富二代,一开始就赢在了人生的起跑线上,有Google富爸爸撑腰,但是发展过程还是比较坎坷。

2016年发生了什么?为什么又重新热起来了呢?

我们来看看Go语言的历代版本

2013年5月—Go1.1:这个版本的Go致力于增强语言特性(编译器、垃圾回收机制、映射、goroutine调度器)与性能。

2013年13月—Go1.2:"Three-indexslices"Go1.2之前,切片的参数只允许有两个,在Go1.2中因为引入了新的语法,即支持第三个参数,可以调整切片的容量....

2014年6月—Go1.3:堆栈管理在此版本中得到了重要改善。

2014年12月—Go1.4:该版本主要侧重于实现工作、改进垃圾收集器并为在接下来的几个版本中推出的完全并发收集器奠定基础。

2016年2月—Go1.6:增加对于HTTP/2协议的默认支持,再一次降低了垃圾回收器的延迟runtime改变了打印程序结束恐慌的方式。现在只打印发生panic的goroutine的堆栈,而不是所有现有的goroutine

2016年8月—Go1.7:Context包转正,垃圾收集器加速和标准库优化

.....

"垃圾回收器被完全重新设计实现",也叫GC。这个是重点,Go在吃了这么多年苦后,也虚心学习古老的GC技术,让它能重新回暖。

在随后的几个版本中,Go都把GC的优化放在改进的重心点。

语法是构成编程语言的基石,Go语言的语法与其他语言相比来看是比较简洁的,但也说明了Go语言的可读性或许不如其他语言强。以下列出了几点Go语法的特性:

在C/C++、Java的语法中,我们通常需要自己输入分号来结束一个语句,但Go语言的代码编写中,无需我们手动输入分号。Go中的词法分析器会根据规则来自动检测是否需要插入分号(然而分号不会在你的代码中出现),通常情况下,在你按下回车后就会自动在行尾插入分号。

const(a=iota//a=iota=0b//b=1c//c=2)2.1.4函数的返回值Go中的函数可以返回多个值,并且多个返回值的类型可以是不同的。在有需要的情况下我们给返回值命名,返回值得有意义才可以。除此之外,Go语言的多返回值也可以用来进行异常处理,函数调用出现错误可以返回一个error类型的值来指示错误。下面这段就展示了Go中多返回值的特性和相比单返回值的优势。

funcWrite(b[]byte)(nint,errerror){...}

Write就是该方法的名称,那么后面的这个括号的n和err就是两个返回值,分别是int和error类型。

在Go语言中其实并没有我们传统意义上类似trycatch这种错误处理的工具和包。在Go中错误通过一个内置的结构类型来表示,程序员在使用这个接口的时候不仅可以用来判断错误,还可以自定义错误类型,提供一些上下文等。

Go语言采用了把错误看作一个值的方式来进行处理,再加上Go语言多返回值的特性,这样使得函数在返回正常值的同时也能返回错误。Go确实也提供两个可以来处理异常的内置函数(panic和recover),但这两个内置函数大多用来处理不可恢复的错误,并且各自都有一些特点并不像error那么通用。所以Go语言错误处理方式是相对繁琐一些的,但这种直截了当的显示设计让代码更加清晰易读抵消了它的冗长性。

typeerrorinterface{Error()string}2.1.6语法糖语法糖(Syntacticsugar)这个名字挺有意思的,是一个应该的计算机科学家发明的词。指在编程语言中添加的一些语法,这些语法对语言本身的功能没有什么影响,而且还能为编程者提供便利。

在一行代码中对进行多个变量的赋值操作,例子如下:x,y=y,x//交换x和y的值

当我们在定义一个函数,有两个返回值,但实际上只有一个用得上,那我们还可以用_将不需要的值忽略。例子如下:

funcdivide(a,bfloat64)(float64,error){ifb==0.0{return0.0,errors.New("divisionbyzero")}returna/b,nil}funcmain(){result,_:=divide(10.0,2.0)//只关心结果,不关心错误fmt.Println(result)}除此之外还有一些其他语言也会见到的一些语法糖就不一一列举了,比如:自增(++)自减(--)、breakcontinue等。总之这些语法糖可以让人们在使用Go语言编程时使代码更简洁清晰,减少代码冗余,让语言更具功能性更优雅。

在Go语言中官方给出的数据结构有:数组(Array)、切片(Slice)、映射(Map)。数组和其他的编程语言类似,用于存储同一类型和固定长度的连续数据结构。切片像是更灵活的数据结构,可以看做是可动态的扩容和缩减数组。映射也与其他编程语言类似,是用于存储键值对的一个哈希表。

相比之下,Go语言没有像链表、队列、栈、树等一些常用的数据结构,但Go语言的这三个基本数据结构足够强,可以用他们来实现这些数据结构。这也和Go语言的保持语言简单避免语言过度臃肿的设计理念相符合。

在几年前的Go是没有泛型这个概念的,直到2022年Go语言的核心团队在Go1.18版本中引入了泛型的概念。初次看见Go泛型,文档给出的很多概念:Typeparameter(类型形参)、Typeargument(类型实参)、Typeconstraint(类型约束)、泛型类型(Generictype)......初次看见啊这些概念时候就算是翻译成中文大概率也不明白他们分别用处和意义是什么。

我们引入一个非常经典的泛型函数例子来介绍类型形参、类型实参和类型约束:

funcPrint[Tany](s[]T){for_,v:=ranges{fmt.Println(v)}}ints:=[]int{1,2,3}Print(ints)//在这里,int是类型实参我们再引入一段代码来解释什么是泛型类型:

packagemainimport"fmt"//定义一个泛型切片类型typeGenericSlice[Tany][]T//添加元素到泛型切片func(s*GenericSlice[T])Append(valueT){*s=append(*s,value)}//获取泛型切片中的指定元素func(sGenericSlice[T])Get(indexint)T{returns[index]}funcmain(){//使用int类型实例化GenericSliceintSlice:=GenericSlice[int]{}intSlice.Append(1)intSlice.Append(2)fmt.Println(intSlice.Get(0))//输出:1fmt.Println(intSlice.Get(1))//输出:2//使用string类型实例化GenericSlicestringSlice:=GenericSlice[string]{}stringSlice.Append("Hello")stringSlice.Append("World")fmt.Println(stringSlice.Get(0))//输出:Hellofmt.Println(stringSlice.Get(1))//输出:World}typeGenericSlice[Tany][]T这段代码就是定义了一个泛型切片类型,那么GenericSlice个泛型类型,该类型可以存储任何类型的元素。如果我们想强制该类型只能存储int、float64和string的数据可以使用这样的方式来定义泛型类型:typeGenericSlice[Tint|float64|string][]T,这里面的int|float64|string也叫做类型约束。

后面的代码就是一些对于不同的类型在泛型切片类型中的具体实例化和使用。除此之外与其他语言相比,在Go中,我们可以使用泛型隐式调用接口并且泛型支持类型判断。假设我们现在有一个Adder的接口,定义一个Add方法,实现了Add方法的类型可以被认为是Adder类型。

packagemainimport"fmt"//Adder接口定义了Add方法typeAdderinterface{Add(a,bint)int}//IntAdder实现了Adder接口typeIntAdderstruct{}func(IntAdder)Add(a,bint)int{returna+b}//使用泛型函数调用Add方法funcsum[TAdder](a,bint,adderT)int{returnadder.Add(a,b)}funcmain(){adder:=IntAdder{}result:=sum(1,2,adder)//类型推断,不需要显式指定IntAdder为Tfmt.Println(result)//输出:3}IntAdder隐式实现了Adder及接口,因为他定义了接口要求的Add方法,这就是隐式接口实现。下面的代码,我们要调用sum函数时候,我们不需要显示地指出IntAdder是T的类型,编译器可以自动判断出来。

这些就是Go泛型中一些比较基本的用法和概念,更多的关于泛型的内容不在这里赘述了。

Go语言拥有强大的编译器工具链,其中包括gobuild、gofmt等,这些工具在给不用平台的用户提供了一致的体验和操作。Go语言的标准库包中含有大量的操作系统抽象,如文件操作系统、网络通信等。Go语言能在Winddows、Linux、MacOS三个不同的操作系统下进行交叉编译都离不开上面这些工具。

一个简单的例子,如果我们在MacOS环境上开发的,而且希望编译一个能在Windows上运行的程序,我们可以在gobuild之前加上这么一串代码即可:GOOS=windowsGOARCH=amd64gobuild。这些特性使得Go语言在开发分布式系统和微服务时尤为受欢迎,因为它们允许开发者在一个平台上开发和测试,然后轻松部署到其他平台上运行。

Go语言的工具有很多,可以分为一下几类:

网络上也有很多Golang的优秀辅助工具,比如Lancet(柳叶刀)

lancet受到了javaapachecommon包和lodash.js的启发,lancet有很多有帮助的特性。

Golang的生态系统怎么样?都在使用Go做什么,一般搭配着什么来使用呢?这里收集了一些统计图。

可以发现Go的新版本非常多人用,大部分人都愿意使用Go的新版本。而与此相反的Java语言就有明显区别。

要知道Java8可是2014年3月18日发布的,这个版本是一个伟大的里程碑,它为Java带来了更现代,更强大的功能。同时这也是不幸的事情,就连MaritvanDijk(JetBrains的技术布道师和JavaChampion)都说:

“It’sunfortunatetoseesomanypeoplestillusingJava8(andolder).Iwonderwhat’skeepingthemfromupgradingtonewerversionsandgettingaccesstogreatnewlanguagefeatures,andhowwecanhelpthemmigratetheircodetonewerJavaversions.”

译文:很遗憾看到这么多人仍在使用Java8(和更早版本)。我想知道是什么阻止了他们升级到新版本,让他们无法使用出色的新语言功能,以及我们如何帮助他们将代码迁移到新的Java版本。

从Go版本使用情况和Java版本使用情况对比发现,Java是"恋久"的、Go是"追新"的,这也体现出了Go具有出色的向后兼容性。

Golang的优质中文社区比较稀少。

稀土掘金也是一个可以讨论学习Golang的网站,还有奖励可以拿,很推荐。尽管社区数量有限,但这些资源仍然可以帮助我们深入学习和探索Golang。

谈到Golang的并发和性能,常常会听到两种声音:

甚至有很多争论的声音。我认为,我们对这两个说法都不要相信,而是应该考虑到,从各自的立场出发的话,他们都是有理由的。我们自己睁眼看,动手做,亲自去体会即可。那谈到Go的性能,我们从何谈起呢?从Goroutine和Channel谈起吧!

Go的设计初衷之一就是要简化并发编程,于是它引入了协程(Goroutine)和管道(Channel)这两个特性来控制并发。它们都是什么呢?怎么就简化了并发编程了呢?我们先来看看他们分别都是什么。

p.s:我这里所提到的协程和管道,特指Go语言中的Goroutine和Channel。

谈到协程,我先简单介绍进程和线程。如果都用一句话解释的话,可理解为:

一个进程可以有多个线程,它们可以共享所属进程的内存,不需要内存管理单元处理上下文的切换,既然有了相比于进程更为轻量的线程,那么Go为什么还出现了协程呢?大体是因为,每一个线程所占用的内存空间还是比较大的,还有就是CPU在多个线程间来回调度的时候,额外的开销还是比较大的。这些在Go语言看起来,都不能忍受。

所以它的调度器使用与计算机CPU数量相等的线程来减少线程间的频繁调度,就省去了线程间来回调度的额外开销。同时又在线程上面开了多个Goroutine来减少执行的开销,提升效率。

就好比,以前一个工厂(进程)只有一条流水线(线程),老板想要提升效率,增加了一个工厂中的流水线数量。但是需要工人(CPU)来回在几个流水线上加工对应的产品(执行程序)。而现在呢?一个工厂中有几个工人就有几条流水线,每条流水线上有多个产品需要加工,一个工人只负责一条流水线,就省去了工人跑来跑去的麻烦。

简单了解了什么是Goroutine,那么什么是Channel呢?由于Go语言推崇使用通信的方式来共享内存,底层抽象出了一个更高级的数据结构Channel用于协程间的通信,使模块间解耦并且致力于让并发变得更简单。

比如下面是利用Channel实现的,在两Goroutine中交替的输出奇数和偶数:

可以看到,我们利用Channel,安全的在两个Goroutine之间传递了信号,使得并发在较为安全的前提下,变得如此简单。基本了解了Goroutine和Channel后,我们再来看看,Go现在的调度模型,是如何高效调度的。

如上图所示,Goruntime的调度器维护了一个全局的协程队列,并且给每一个线程M上绑定了一个处理器P。每个P的本地又维护了一组协程队列。那么什么是HandOff和StealWork呢?

我们可以将StealWork看做是,某个处理器中没有协程需要处理了,与之绑定的线程空闲了,并且全局也没有需要执行的协程了,那么去帮帮兄弟线程。从兄弟那里去“偷”几个协程过来帮他执行。所以通过工作窃取方式,可以对任务进行再分配实现任务的平衡。

如下图所示:

当简单了解了Go语言runtime的调度模型,来看看它大致是如何进行内存管理。

要来管理内存,首先得了解Go的内存模型是怎样的。

首先了解一点点前置知识:Go语言为了防止内存碎片化,采用了分级隔离策略来提高内存的利用率。将其最小内存管理单元分为了68个级别的mspan。了解了这个点,来看看上图。为了提高访问效率、减少锁竞争等问题,Go语言将其内存分成了由HeapArena、MCentral、MCache构成的三级缓存。

我们从下至上,依次来看看是哪几层:我们将物理内存转化为虚拟内存后,Go语言将这大小为256TB的庞大虚拟内存转变成了222个大小为64MB的HeapArena。为什么刚好这么多个呢?因为虚拟内存最大为256TB,而222*64MB,刚好等于这么大的内存。这所有的HeapArena共同构成了Go语言的堆内存MHeap。

然后程序想要分配内存,需要获取MSpan,因为这是最小的内存管理单元。想要获取MSpan,我们得从HeapArena中获取,但由于有68个等级的MSpan,想要获取对应等级的MSpan,得挨个遍历才能获取。为了快速得到想要的MSpan,所以创建了一个中心索引缓存MCentral。将每个等级的MSpan,分了需要GC和不需要GC(scan和noscan)扫描的两条链表,共134条,为什么是134条呢?因为其中有一个等级的mspan很特殊——0级的MSpan——它是直接放入HeapArena中的,所以67*2,只有134条。

那么想要获取MSpan,就优先会从MCentral中获取了。但是MCentral是全局的资源,是多个协程共享的,需要加锁才能安全的访问。为了防止过多的锁竞争,导致锁饥饿的问题,在每个M上添加了一个本地缓存MCache缓存,其中包含每一级别的MSpan各两个,也是分scan和noscan两种,那么就是68*2,总共136组。有了MCache,这样就不需要每次都从全局中心索引中去获取MSpan了。

当你大致了解了Go语言的内存模型,来看看它是如何进行内存分配的。

Go把所有需要申请内存的家伙都称为对象Object,对于直接分配到堆上的对象,Go会根据对象的大小进行分配。它将其分为三个等级的对象:

进行分配时,根据对象的大小,有不同的操作。如果大小为0,会直接分配一个固定的地址zerebase给它。

其次会检查大小是否在32KB以内。如果是小于16B的tiny对象,并且不需要被垃圾回收器扫描,它会尝试通过MCache中的tiny字段来分配地址。

如果tiny字段已满,它会将tiny放入MCache一个2级的MSpan中,并为tiny分配新的内存地址。如果2级MSpan也满了,将从MCentral中交换一个。

另一种情况是会根据对象的大小,从MCache中获取一个能容纳它的最小级别的MSpan来分配地址。如果对应级别的MSpan满了,则会加锁从MCentral中交换一个对应级别的MSpan。如果连Mcentral对应等级的MSpan链表也满了,将从HeapArena中换一组对应大小的MSpan。如果这一个HeapArena也满了,将向操作系统申请一块新的虚拟内存单元HeapArena。

最后对于大于32KB的对象,首先会计算对象的大小,然后申请对应大小的0级MSpan后,直接放入HeapArena中。

从上面的分配过程,也能看出Go是对内存进行分级和分层管理的,这样不仅有助于提高内存的利用率,对内存的分配效率也有显著的提高。那么分配完内存,如何清理不需要的内存呢?

这就得来谈谈Go的垃圾回收了(GarbageCollection,也称为GC),Go目前采用的是“三色标记法的标记清除+混合屏障”的垃圾回收策略。那么什么是三色标记法呢?什么又是混合屏障呢?

三色标记清除法源于标记清除,思路非常简单:扫描内存标记出需要清理的对象,然后在回收掉即可。其中核心是标记,只要我们正确标记了需要清理的无用对象,清扫其实很简单。

其过程简单来说就是:一开始将所有对象都置为白色,表示暂时无用的对象。然后将所有的GCRoot对象放入灰色集合,利用可达性分析扫描GCRoot能够到达的对象,将能到达的对象放入灰色集合的同时将扫描完成的对象放入黑色集合。

当灰色集合中的所有对象都被扫描分析完成时,意味着标记结束了,程序中只有白色和黑色两种颜色的对象,然后清理掉还是白色标记的对象即可。

其实这个过程,即使使用普通的“标记”方式,也能达到类似的效果,那么为什么还要费尽心思搞成三色标记法呢?其实是为了更好的增加混合屏障,让垃圾清理的效率和标记的安全性做一个平衡。

这里提到的混合屏障,包含插入写屏障和删除屏障两种。为什么要引入两种屏障呢?这个核心点还是在于Go想要提升垃圾回收的效率。

我们可以试想一下,如果垃圾回收的过程和用户程序是串行化执行的。当需要进行垃圾回收的时候,直接暂停所有正在执行的程序,标记清理完成后再继续执行。这个过程叫做STW(StopTheWorld),如它的名字表明的一样,暂停这个“世界”,我要开始进行垃圾回收了!

这其实就是Go最早期的垃圾回收策略。这样串行化的好处很明显:实现简单、安全...唯一的缺点恐怕就是不够快。但这唯一的缺点也是它致命的缺点,Go不断迭代的垃圾回收器,都在致力于平衡效率和安全的关系。

在三色标记的过程中,用户的程序正和垃圾回收在并发的执行,突然有个还未扫描的灰色对象的下游对象指向了一个已分析扫描完成的黑色对象。这样在此轮扫描结束后,那个乱跑的对象,就会被当做垃圾清理掉,但其实它是一个有用对象。

所以为了限制这样乱跑的对象,就增加了一种“插入”写屏障的机制。即当有对象莫名其妙插入黑色对象上时,直接将其设置为灰色,代表一会还要检查它。那么在此轮扫描结束后,最终也能保证不会误清理乱跑掉的对象。

但是Go对栈要求要有非常快的响应速度,以供函数调用频繁的入栈和出栈,在栈上使用屏障会影响性能,所以这种屏障只能在堆上使用。如果栈上不能使用屏障机制,那么有堆对象要跑到栈上,或者有栈对象跑到堆上,就可能会出现误清理的情况。

所以引入了删除屏障,配合写屏障一起使用,在堆上开启了混合屏障。

至于为什么呢?稍后解释,先来看看什么是删除屏障。当有一个待扫描对象突然删除了它某一下游对象时,防止跑掉的下游对象被添加到某已扫描的黑色对象上,我们将被删除的下游对象设置为灰色,放入灰色队列中等待分析扫描。

拥有了混合屏障,对于栈空间和堆空间上的对象,有这些可能:

所以当你了解了有这几种情况的时候,我们可以简单的理解为:

其实这些信息都对,我不反驳,但一个人和一件事能存在于世,定有他的作用与道理。语言技术也是一样的,Golang可能如大家所言。

在企业里,语言并没有那么重要,重要的是问题的解决方案,团队招人的时候,招的基本都是后端Title,并没有限定语言为Golang,如果语言那么重要,干嘛不精确一点。况且据我耳闻,经常听到大家谈论某一技术细节,基本没有拿着xxx语法、xxx框架的使用来讨论的。

没有烂代码,只有垃圾代码,只有屎山代码,不知道我说这话你认不认同。在公司里,我见过很优秀的Golang代码,同样也见过很烂的Java代码。这你能说,代码写的烂,语法很特殊,就能让这个人的开发习惯、编码风格从好变成坏?从乐色变成牛逼?

说平时写代码根本用不到它并发的,我只能说你对它还不够熟悉。开箱即用的Goroutine、Channel、各种同步工具,你都用不明白,还能把什么更高级的并发工具用好呢?我自己写过的两个较大的需求:批量推送、智能创建,列这个名字的目的,就是想说这就是很基础的业务逻辑,但无一没有用到它提供的并发工具,并且最终的性能,都是真金白银跑出来的。

关于其他的,其实我也不想多说,其实它好与不好,一点都不重要。每个人都有缺点和优点,人生如此,技术亦然。如果你非要跟我争论谁好谁不好,没关系,一切以你为准。

THE END
1.个人定制服装app排行榜前十名个人定制服装app有哪些?几款个人服装定制软件推荐个人装修接活app衣服定制app服装制版软件网上定制衣服app个人记账软件服装搭配软件app服装cad软件卖服装专用的拍图软件男装搭配衣服软件服装店收银系统软件网上找装修工人app相机换装古风服装软件可以换服装照相软件服装排版软件可以定位别人手机位置软件服装穿搭app业主找装修工人https://m.pianwan.com/s/zj-179716
2.探索最佳服装3D设计软件:全面比较与评测综合来看,不同的服装3D设计软件各有特点和优势,设计师们可以根据自己的需求和工作流程选择适合的软件。无论是Marvelous Designer、Clo3D还是Browzwear,它们都为服装设计师们提供了强大的工具和功能,帮助他们更好地实现创意和设计。随着科技的不断进步,相信未来会有更多创新的服装3D设计软件涌现,为时尚设计行业带来更多可能https://www.eeff.net/article-23777-1.html
3.可以自己设计衣服的手机软件(盘点:21个超赞的T恤模板创建网站1、穿针引线app是可以发自己原创图做衣服的。 在穿针引线app中可以发表自己的原创图稿来交流,也可以观看别人的图稿来吸收经验。 穿针引线是一款专为服装行业服务的手机app,应用不仅实用于服装设计师,也实用于服装贸易商,这款应用是服装行业的入口。 2、Infinitee目前提供T恤、卫衣、帽衫、帆布包等产品载体,用户可以在https://www.bb1314.com/news/50059.html
4.关于宣传方案范文(通用14篇)15.T恤设计大赛 目的:助同学展开心扉,展开学子健康向上的精神面貌。 时间:4月10日-4月22日 地点:综合楼206 承办单位:计算机与控制工程学院 16.“让希望见到晨光”心理话剧大赛 目的:在宣传普及心理健康知识的同时为学生搭建起锻炼心理品质、提高心理素质的平台。 https://www.wenshubang.com/fangan/1752664.html
5.服装网络营销策划书7篇个性T恤设计制做:把 喜欢的图案印成时尚T恤,穿出个人个性. 情侣T恤(情侣装):穿上情侣T恤(情侣装),把爱穿在身上,贴 心收藏.每件情侣T恤(情侣装)都为 情侣们倍增浪漫情怀. 亲子T恤(亲子装): 每一件亲子装都有浓浓的温馨,可在亲子装印上宝宝名字.更显亲子装的亲子效果 https://www.ruiwen.com/yingxiaocehuashu/5497152.html
6.美客多新手指南所有的国际海运和清关都将由MercadoLibre自己管理。 ●美客多2020中国招商战略● 由于Mercado Libre缺少家居、户外运动等品类卖家,因此平台在中国的招商战略将侧重于吸纳稀缺品类的卖家入驻,同时这些品类在依赖进口的拉美地区有着较大的市场。此外,作为注册用户超2.8亿的平台,Mercado Libre中国跨境卖家的数量大概还不到1000https://www.cifnews.com/guide/mercadolibre
7.2018年创业必备的105种工具Teachable可以让你轻松发布自己的课程、将其出售并管理现有的学生。它是一个托管型服务,只要你完成注册,课程就可以上线。通过在Teachable上发布课程,平台上的课程讲师已经赚到了1亿多美元。它还采用了联盟营销的手法,这样其他的人也可以通过推广你的课程得到酬劳。个人课程设计者、机构以及企业(如宾夕法尼亚大学法学院https://lusongsong.com/info/post/10026.html
8.c2b的电商平台有哪些一、c2b电商平台有哪些 1. 自主定制平台:消费者可以根据自己的需求定制商品,这种平台主要是针对一些特殊需求的消费者,比如定制手机壳、定制t恤等。 2. 众包平台:这种平台让消费者可以参与到产品的开发和设计当中,通过众包的形式,满足消费者个性化的需求。例如,一些众包设计的网站,让消费者可以提交自己的设计方案,最终https://h.chanjet.com/ask/1832a7b237327.html
9.不仅仅是T恤个性化定制,“自做自售”想做“柔性制造的入口平台”除此之外,供应链也是一个很大的考验,平台开发了专门的系统“自做自售”供应链的工艺和研发大概用了两年左右的时间解决,只有把后端的供应链的环节完善才能真正做到按需定制。 在“自做自售”app上,用户可以自行根据平台提供的简单素材做T恤设计,自己购买或者放在平台上卖给别人,主流价位99元,也有少量在59元的价位,平https://36kr.com/p/5039362.html
10.www.lmwa.com/aplpage37670.html尽管在对阵森林狼的比赛中,库明加没有延续上一场的火热状态,全场15投仅6中贡献13分,但勇士仍然选择对他保持耐心。 “如果我们想变得更好,你已经在对火箭的比赛中看到了他的能力,我们需要他展现出最好的自己。无论是战术安排还是阵容搭配,我们都必须找到释放他能力的方式,让他能突入禁区、给对方防线施压。他是我们https://www.lmwa.com/aplpage37670.html
11.Teespring众筹T恤定制平台海淘可转运Teespring将众筹模式运用到T恤定制中,让整个过程变得更流畅简单。用户上传T恤设计图案并设定好价格和目标件数,将链接分享至社交网络让感兴趣的人参与购买。当所售件数超过目标件数时,T恤才会确定印制。 Teespring不是T-shirt品牌,它只是一个网站,设计了一个简单的T-shirt设计页面,任何人都可以在这个平台上按照自己的https://www.178good.com/buy/teespring/
12.活动策划范文精选15篇国学经典是中华传统文化的精华,内容博大精深,蕴含着丰富的人文精神。在学生中开展国学经典活动,有利于学生识记和领悟国学文化的精髓,促成我校学生良好思想道德的形成,提升我校学生的人文素养和审美情趣,进而形成正确的人生观、价值观和世界观。同时增强民族自信心和民族自豪感。 https://www.yuwenmi.com/fanwen/huodongcehua/3633404.html
13.素材中国www.sccnn.com素材CNN素材中国,素材CNN_免费素材共享平台.图片素材图库提供海量素材,图片下载,设计素材,PSD源文件,矢量图,AI,CDR,EPS等高清图片下载http://www.sccnn.com/
14.这11个基本免费的LOGO设计平台,总有一个适合你优设网这11个基本免费的LOGO设计平台,总有一个适合你 LOGO 设计一直都是一个重要的设计分支,它在品牌设计领域几乎是不可替代的核心存在。每一个品牌都需要一个独一无二,具有代表性的LOGO作为品牌宣传、营销的核心。 当然,LOGO设计的方式有很多种,许多有经验的设计师都有一套自己的LOGO设计流程,但是这些流程不尽相同。https://www.uisdc.com/11-logo-design-websites/
15.orimuse——源自你的灵感2、我们做什么——专业的T恤定制平台 个人T恤定制服务:我们为每个人提供最贴心便捷的T恤定制服务,通过手机APP或者网站设计并上传创意,创造属于自己的潮流。具体操作流程为:用户手机客户端或网站设计→下单购买→生产配送→用户收货。 团体T恤定制服务:想让自己的团体更有凝聚力?就来orimuse,满足团队T恤的需求,展现集体的https://weibo.com/p/1001603831952720398504?mod=zwenzhang
16.T恤定制T恤定制的工艺很齐全,尤其是丝网印方面!他们会主动的根据我们的设计选择相匹配的印花工艺,力求百分百还原设计,真正的匠心定制!几次定制体验都很棒! 张都 顺顺留学CTO 以往都是在淘宝下单,在朋友介绍下尝试在T恤定制。T恤定制确实有所不同,他们对每件T恤、每件卫衣、每个印花图案的态度可以用最专业来形容。 https://www.shangxinzhushou.net/
17.“阳江十八子”总裁李积回——“刀王”的音乐江湖界面新闻·J《粤商》:音响博物馆这个平台带给你哪些收获? 李积回:博物馆使我结识了很多朋友,政界、音乐界、商界的都有。做企业不可能认识这么多人,很多发烧友不经意地带给我商机,可谓是爱好和事业双赢。 《粤商》:针对博物馆的未来,你有何打算? 李积回:我希望将来建一个像悉尼歌剧院一样的地标性建筑做博物馆,并依托于博物https://www.jiemian.com/article/224423.html
18.卓色网同时它也是一种设计师的平台,设计师可以把自己的作品在卓色展现出来,卓色的DIY满足越来越多追求个性时尚的消费者,因为备受青睐。 卓色网(http://www.drawshirt.com)简称卓色,曾用名着色,属深圳市卓色科技有限公司旗下,是全球服装创新理念下诞生的第一批T恤DIY定制平台,总部地点位于中国经济发展前沿城市-深圳,卓越而https://www.douban.com/group/topic/32961227/?from=tag_all
19.平台,适合销售手工制品西班牙有哪些合适的平台可以做新卖家在前12个月内免收平台费用,并且可以免费列出前50件商品。 活跃的商户可以邀请最多五个其他卖家,并从他们的销售中获得奖金,提供了另一个收入来源。 Handmade是Novica旗下品牌,后者与国家地理学会和草根企业基金等大型国际品牌和组织都有合作关系。 缺点 https://blog.csdn.net/2401_87032782/article/details/141927219
20.如何在Fiverr官方网站赚钱?fiverr平台怎么赚钱?兼职接单教程攻略如果你擅长设计Logos,网站建设或视频制作。或者,你可以通过其社交媒体或人力资源策略帮助企业。无论你的天赋如何,Fiverr都使你有机会与其他在线世界分享这些技能。最重要的是,你可以在做自己喜欢的事情的同时赚钱。 Fiverr 平台上有两种角色: Fiverr是一个双向交流平台,这意味着买卖双方都有机会互相寻找。买方可以浏览自https://www.xujiahua.com/8946.html
21.县域电商平台建设(通用6篇)第二步:建立电商平台,制定平台管理和服务规范,吸引商城商家加盟,提高平台在本地区的市场知名度,开展会员管理【商家和客户】,建立售前售后服务网络,建立产品采购网络,建立产品配送网络,培训员工,将电商平台建成一个专业的网上商城。 第三步:在本地区有较强的知名度,打出自己的品牌,在平台内容上有更进一步的充实,争取https://www.360wenmi.com/f/fileidvmb61z.html
22.摄影活动范文12篇(全文)如杭州群众文化网2011年举办的《杭州记忆》新老照片征集摄影大赛,由于网络平台设计合理,上传照片简单方便,使各参赛选手的参赛作品一览无余,在上传自己参赛作品的同时,还可以欣赏到其他作者珍藏的优秀作品,因此得到大家热烈响应,好评如潮。 3.调动摄影爱好者的积极性。相对于传统的摄影活动仅仅局限于有限人群和有限作品的https://www.99xueshu.com/w/ikeycejy572h.html
23.T社T社定制的工艺很齐全,尤其是丝网印方面!他们会主动的根据我们的设计选择相匹配的印花工艺,力求百分百还原设计,真正的匠心定制!几次定制体验都很棒! 张都 顺顺留学CTO 以往都是在淘宝下单,在朋友介绍下尝试在T社定制。T社定制确实有所不同,他们对每件T恤、每件卫衣、每个印花图案的态度可以用最专业来形容。 https://www.tshe.com/semab/tshirt/c
24.定制设计开店,只需鼠标一点.自己做设计师,件件绝版,1件起移动权重: ALEXA排名 世界排名:0 预估日均IP≈0 预估日均PV≈0 www.idx.com.cn页面TDK信息 标题(Title) 70个字符 (一般不超过80字符) IDX爱定客—创意,便捷的多品类个性化在线定制平台! 关键词(Keywords) 255个字符 (一般不超过100字符) 爱定客,IDX,定制鞋,帆布鞋,滑板鞋,个性定制,个性签名,t恤定制,量http://www.linghangpaimai.com/tools/seo-lookup/www.idx.com.cn
25.官网登录平台入口地址战马获取方法:content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />战马系统来袭“其疾如风,其徐如林;侵掠如火,不动如山”,四大系列战马任你选择,英雄们快来认领你的专属坐骑吧。 三国之刃今天正式更新了战马系统,相信皮梁玩家在3.0.0版中已经玩的十http://www.xzz2022.top/876793.html