小白也看得懂Goroutine | 青训营

81 阅读3分钟

引言

当我们计算1-200000有多少个素数时,依照之前所学知识写出来的代码,相当于是一个人一个数一个数的计算,这样运行时间就很慢。当我们使用goroutine(协程),就相当于多个人同时计算,极大地提高了运行速度。

进程和线程的关系

1.进程就是程序在操作系统中的一次执行过程,是系统资源分配和调度的基本单位。在任务管理器中可以查看当前系统的进程。

image.png 2.线程是进程的一个执行示例,是程序执行的最小单元。而一个进程可以至少有一个线程。

3.一个程序也至少有一个进程,也可能多个,也就是网上所说的多开。

并发和并行的区别

  1. 程序在单核运行,就是并发
  2. 程序在多核运行,就是并行

并发:假如有十个线程,在单核运行,所以它们是cpu在极短时间内执行一个线程,随后切换至另一线程执行,因为时间极短,所以感觉像是在同时运行,实际上不是。

并行:因为CPU都是多核的,假设16核CPU,执行10个线程,cpu1会执行一个线程,cpu2会执行一个线程......线程各自在不同CPU同时执行。

区别:用比喻来形容,并发相当于十个人玩一台电脑,而并行是十个人每个人都有电脑玩

Go协程和主线程

在Go中,一个Go主线程可以开启多个协程,此处的协程,可以理解为一种轻量级的线程。所以协程和主线程的关系其实和上文提到的进程和线程的关系类似。

代码演示

在下面的代码中,我试图同时输出“hello,world”和“hello,golang”。其中我使用了time包中的time.Slepp(),作用是间隔一秒输出,以便观察输出顺序。先让我们看一下没开启协程的结果:

image.png 可以看出它是先输出完“hello,world”再输出“hello,golang”的。下面我们看一下开启协程后:

image.png 我们很容易看出,两句话是同时输出,这就是开启了协程的效果。重点:程序的结束是以主线程为主,当主线程运行完后,不管协程是否运行完,都会结束。下面我们将print()函数打印次数改为10,可运行结果出来,print()函数还是只打印了5次。

image.png

资源竞争问题

如图,图上写了一段代码,求出1-20的阶乘,并放进map中,当我们直接运行后,程序却会报错。这是因为我开启了20个线程,但它们会同时往map中写入,这就造成了资源竞争

image.png

现在,我们需要使用全局互斥锁来解决这个问题。当第一个协程往map中写入,会加锁,写完后则会解锁。后来的协程发现map是加锁的,就会进入队列等待,直到map解锁。这里我们引用了sync包。

image.png 到这里又出现了个问题,上文说过,程序结束是以主程序结束为主。那么,主线程如何知道协程是否结束呢?这里就提出了协程的通讯问题,我们将在下一章提出解决方案,Channel管道)。