一、前言
Go语言在设计时,Java和C ++是编写服务器程序最常用的语言(至少在Google是这样),这是因为使用这些语言可以高效的开发。但是Go设计者们觉得像Java和C++这些语言需要开发者记忆太多的语法和规则,并且需要重复做的事情太多,这导致一些程序员开始转向更加动态,流畅的语言,如Python,但是付出的是损失开发效率和对类型安全检查的缺失。Go设计者们认为应该可以发明一种语言,这种语言集高效的开发、提供类型安全检查、简洁流畅的代码风格与一体,于是Go就诞生了。
二、GoLang并发编程
Go在语言层面提供了内置的并发支持,在Google内部运行在多核心cpu机器之上并拥有高并发流量的web服务器程序,是最典型应用。由于C++和Java在语言层面对并发的支持不是特别好,所以使用它们编写高并发程序都不是特别容易。
传统的线程模型,比如经常使用Java、C++、Python编程的时候,需要多个线程之间通过共享内存(比如在堆上创建的共享变量)来通信。这时候为保证线程安全,多线程共享的数据结构需要使用锁来保护,多线程访问共享数据结构时候需要竞争获取锁,只有获取到锁的线程才可以存取共享数据。
Go中也提供了这种低级同步原语-锁,比如互斥锁、读写锁、条件变量等,但是Go的并发原语 - goroutines和channels 提供了一种优雅而独特的结构化开发并发软件的方式。Go鼓励使用通道在goroutine之间传递对共享数据的引用,而不是明确地使用锁来保护对共享数据的访问。这种方法确保在给定时间只有一个goroutine可以访问共享数据。这个理念被总结为:不要通过共享内存来通信,而要通过通信来共享内存。
Go中并发模型采用了channel,体现为CSP的一个变种。之所以选择CSP,一方面是因为Google的开发工程师对它的熟悉程度,一方面因为CSP具有一种在无须对其模型做任何深入的改变就能轻易添加到过程性编程模型中的特性。比如,对于C语言,CSP可以一种最长正交化(orthogonal)的方式添加到这种语言中,为该语言提供额外的表达能力而且还不会对该语言的特性施加任何约束。
在其他语言比如Java中线程模型的实现是一个操作系统内核线程对应着一个我们使用new Thread创建的一个线程,而由于操作系统线程个数是有限制的,所以限制了我们创建的线程个数,另外当线程执行阻塞操作时候,线程要从用户态切换到内核态执行,这个开销是比较大的;而在golang中线程模型则是一个操作系统线程对应多个goroutine,用户可以创建goroutine个数只受内存大小限制,另外上下文切换发生在用户态,切换速度比较快,并且开销比较小,所以go中一台机器可以创建百万的goroutine。
在Java中当我们编写并发程序时候我们需要在操作系统线程层面进行考虑,但是在go中,我们不需要考虑操作系统线程,而是需要站在goroutine和channel上进行思考,当然有时候也会在共享内存上进行思考。
另外大家或许听说过反应式编程、反压等概念,比如Java中rxjava库提供的功能,而在go中我们可以使用channel轻易的实现管道,让多个goroutine进行任务进行fork然后进行join,以及轻易的实现反压功能。
Golang的应用范围越来越广,大名鼎鼎的容器化技术docker以及号称分布式操作系统的k8s底层实现就是Golang来实现的,随着技术的快速发展,我们只有不断迭代自己的技术栈,才能不会被淘汰,而golang将会是未来应用场景比较多的一种语言,各大公司也将会要求必须掌握golang来进行快速开发高并发应用程序。
最后推荐《Go并发编程之美》 专题,该专题从下向上,基础篇首先会基于数据竞争的存在、内存访问需要同步、原子变量、讲解并发编程到底难在哪里,然后讲解Go的线程模型、内存模型、goroutine的基础知识;然后深入讲解Go中提供的低级同步原语锁,以及锁的内存模型;接着讲解Go独特的同步原语channel,以及channel的内存模型,以及channel、goroutine、select的结合使用;然后讲解go中的Context包,我们会展开讲解如何传递请求级别的变量、如何使用context在一个goroutine内取消另外一个goroutine的运行;相信学完本专栏,大家对go并发编程会有深入的理解,并会为go中并发编程精髓channel、select、goroutine的搭配功能而惊艳到。