引言
当我们计算1-200000有多少个素数时,依照之前所学知识写出来的代码,相当于是一个人一个数一个数的计算,这样运行时间就很慢。当我们使用goroutine(协程),就相当于多个人同时计算,极大地提高了运行速度。
进程和线程的关系
1.进程就是程序在操作系统中的一次执行过程,是系统资源分配和调度的基本单位。在任务管理器中可以查看当前系统的进程。
2.线程是进程的一个执行示例,是程序执行的最小单元。而一个进程可以至少有一个线程。
3.一个程序也至少有一个进程,也可能多个,也就是网上所说的多开。
并发和并行的区别
- 程序在单核运行,就是并发
- 程序在多核运行,就是并行
并发:假如有十个线程,在单核运行,所以它们是cpu在极短时间内执行一个线程,随后切换至另一线程执行,因为时间极短,所以感觉像是在同时运行,实际上不是。
并行:因为CPU都是多核的,假设16核CPU,执行10个线程,cpu1会执行一个线程,cpu2会执行一个线程......线程各自在不同CPU同时执行。
区别:用比喻来形容,并发相当于十个人玩一台电脑,而并行是十个人每个人都有电脑玩
Go协程和主线程
在Go中,一个Go主线程可以开启多个协程,此处的协程,可以理解为一种轻量级的线程。所以协程和主线程的关系其实和上文提到的进程和线程的关系类似。
代码演示
在下面的代码中,我试图同时输出“hello,world”和“hello,golang”。其中我使用了time包中的time.Slepp(),作用是间隔一秒输出,以便观察输出顺序。先让我们看一下没开启协程的结果:
可以看出它是先输出完“hello,world”再输出“hello,golang”的。下面我们看一下开启协程后:
我们很容易看出,两句话是同时输出,这就是开启了协程的效果。重点:程序的结束是以主线程为主,当主线程运行完后,不管协程是否运行完,都会结束。下面我们将
print()函数打印次数改为10,可运行结果出来,print()函数还是只打印了5次。
资源竞争问题
如图,图上写了一段代码,求出1-20的阶乘,并放进map中,当我们直接运行后,程序却会报错。这是因为我开启了20个线程,但它们会同时往map中写入,这就造成了资源竞争。
现在,我们需要使用全局互斥锁来解决这个问题。当第一个协程往map中写入,会加锁,写完后则会解锁。后来的协程发现map是加锁的,就会进入队列等待,直到map解锁。这里我们引用了sync包。
到这里又出现了个问题,上文说过,程序结束是以主程序结束为主。那么,主线程如何知道协程是否结束呢?这里就提出了协程的通讯问题,我们将在下一章提出解决方案,Channel(管道)。