60

Golang:线程 和 协程 的区别

 4 years ago
source link: https://www.tuicool.com/articles/rIRfuyE
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

国庆越快各位,距离上次发文快两个月了,19年也快结束了。现在的总结更多是放在了 草稿 而没有发出,这次详细分享下在 Go 中, 线程和协程的区别及其关系

协程

协程,英文名 Coroutine 。但在 Go 语言中,协程的英文名是: gorutine 。它常常被用于进行 多任务 ,即 并发作业 。没错,就是 多线程 作业的那个作业。

虽然在 Go 中,我们不用直接编写线程之类的代码来进行并发,但是 Go 的协程却 依赖于线程 来进行。

下面我们来看看它们的区别。

线程的基础介绍,这里请自行网上搜索文章,因为关于线程的优秀介绍文章已经很多。

协程的特点

这里先直接列出线程的特点,然后从例子中进行解析。

协程的调度

上面 第 1第 2

我们来看一个例子:

func TestGorutine(t *testing.T) {
	runtime.GOMAXPROCS(1)  // 指定最大 P 为 1,从而管理协程最多的线程为 1 个
	wg := sync.WaitGroup{} // 控制等待所有协程都执行完再退出程序
	wg.Add(2)
	// 运行一个协程
	go func() {
		fmt.Println(1)
		fmt.Println(2)
		fmt.Println(3)
		wg.Done()
	}()

	// 运行第二个协程
	go func() {
		fmt.Println(65)
		fmt.Println(66)
		// 设置个睡眠,让该协程执行超时而被挂起,引起超时调度
		time.Sleep(time.Second)
		fmt.Println(67)
		wg.Done()
	}()
	wg.Wait()
}
复制代码

上面的代码片段跑了两个协程,运行后,观察输出的 顺序是交错 的。可能是:

65
66
1
2
3
67
复制代码

意味着在执行协程A的过程中,可以 随时中断 ,去执协程行B,协程B也可能在执行过程中中断再去执行协程A。

看起来协程A 和 协程B 的运行像是线程的切换,但是请注意,这里的 A 和 B 都运行在同一个线程里面。它们的调度不是线程的切换,而是 纯应用态的协程调度

关于上述代码中,为什么要指定下面两行代码?

runtime.GOMAXPROCS(1)
time.Sleep(time.Second)
复制代码

这需要您去看下 Go 的协程调度入门基础,请看我之前的另外一篇调度分析文章:

Go 的协程调度机制

如果不设置 runtime.GOMAXPROCS(1) ,那么程序将会根据操作系统的 CPU 核数而启动对应数量的 P,导致多个 M,即线程的启动。那么我们程序中的协程,就会被 分配到不同的线程 里面去了。为了演示,故设置数量 1,使得它们都被分配到了同一个线程里面,存于线程的协程队列里面,等待被执行或调度。

协程特点中的第 3 和 第 4 点。

  1. 执行效率高。
  2. 占用内存少。

因为 协程的调度切换不是线程切换 ,而是由程序自身控制,因此, 没有线程切换的开销 ,和多线程比,线程数量越多,协程的性能优势就越明显。调度发生在应用态而非内核态。

内存的花销,使用其所在的线程的内存,意味着线程的内存可以供多个协程使用。

其次协程的调度 不需要多线程的锁机制 ,因为只有一个线程,也 不存在同时写变量冲突 ,所以执行效率比多线程高很多。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK