Go语言创立之初,CPU多核发展蓬勃,Go语言创始人果断将面向多核,原生支持并发作为Go的设计目标之一语言,所以在Go语言中使用它并发具有得天独厚的优势。那么,什么是并发?说到并发,就会随之而来一系列其他的概念,比如:并行、进程、线程、异步等等。我们经常用C#的Winform程序写一些工具,编译成exe文件。文件执行后,在Windows中会作为一个进程执行。在以前的单核CPU时代,某个时刻只能执行一个进程。对于程序代码来说,不存在两个进程并行执行的可能,多处理器或多核处理器是并行执行的必要条件。如果修改程序,将要执行的任务分解,分解后的每个小模块由一个单独的线程处理,多个线程共享进程拥有的资源。线程可以作为执行单元独立调度,在设备上运行进程。也许它仍然在单个CPU上执行,但是是并行的。Go语言的创始人RobPike曾经说过:并行是关于执行的,而并发是关于结构的。举个生活中的例子:每天排队做核酸分三步:1、排队。2.扫描二维码。3、刺喉。需要做核酸的人在多个团队中排队,多个团队并行处理。每个小组只有一名检查员。先扫码,再戳喉咙。两步完成后,进行下一步。扫码和戳喉都是由不同的人员处理,就像把程序拆分成多个线程来处理一样。并发也可以发生在单CPU上,如上图只有一个团队的情况,但多核或多CPU可以发挥更大的作用。可见,要起到并发的作用,线程和多核是分不开的。虽然创建线程的成本比进程小很多,但仍然不适合大规模创建线程,因为除了每个线程占用的资源外,运行系统调度线程的成本也不小.因此,Go语言创建了goroutines,也叫协程,是由Goruntime调度的轻量级线程。与常规线程相比,它有这些优点:资源占用小,goroutine的Stack初始大小为2k,C#和Java语言中线程的stack都是mega级的,所以goroutine的创建会更快;goroutine是由Goruntime调度的,而不是操作系统,切换速度会更快。Go中如何使用goroutine?很简单,只需要使用关键字go。默认情况下,主程序位于单独的goroutine中。如果函数或匿名函数使用go关键字,则会创建一个单独的goroutine。packagemainimport("fmt""time")functestGouroutine(namestring){fmt.Println("goroutine:",name)}funcmain(){fmt.Println("这是主程序")gotestGouroutine("1")gotestGouroutine("2")gotestGouroutine("3")gotestGouroutine("4")time.Sleep(time.Second)}如果在使用go关键字的函数中使用了主程序中的资源,就会有竞争,看下面的例子:packagemainimport("fmt""time")functest(){counter:=0fori:=0;我<5000;i++{gofunc(){counter++}()}time.Sleep(time.Second)fmt.Println("counter:",counter)}funcmain(){test()}每次运行,counter的值会有所不同,因为每次不同的协程抢夺的公共资源计数器是不同的。为了解决这个问题,需要锁:packagemainimport("fmt""sync""time")functest(){varmutsync.Mutexcounter:=0fori:=0;我<5000;i++{gofunc(){defermut.Unlock()mut.Lock()counter++}()}time.Sleep(time.Second)fmt.Println("counter:",counter)}funcmain(){test()}值得注意的是,使用go关键字执行的函数即使有返回值也会被忽略。如果需要goroutine之间的通信,则需要通道。通道是使用带有关键字chan的make构建的。看下面的例子:packagemainimport("fmt""strconv""time")funcmain(){//使用make创建一个字符通道,通道使用关键字chanmsg:=make(chanstring)//in在异步匿名函数中模拟同步数据,完成一条消息到通道gofunc(){fori:=1;我<=10;i++{ifi==10{msg<-"success"}else{msg<-"一共需要同步10个模块,已经处理完毕"+strconv.Itoa(i)+"pieces"}//每个循环顺序,演示1秒,模拟时间为time.Sleep(time.Second)}}()//在主线程中进度显示form:=rangemsg{ifm=="success"{fmt.Println("synchronizationcomplete")break}else{fmt.Println(m)}}}每隔一秒向主gorourine发送一条消息,主gorourine将消息打印出来。最后总结一下:1.并行是讲执行,并发是讲结构。2.Go语言的并发是基于轻量级的goroutines。与普通线程相比,goroutines有很多优点。3.不同的goroutine之间需要用channel进行通信。频道是用make创建的,关键字是chan。
