线程 协程

进程、线程和协程

进程

进程 (process) 是执行中的程序,这是一种非正式的说法。进程不只是程序代码,程序代码有时候称为文本段。进程还包括当前活动,通过程序计数器的值和处理器寄存器的内容来表示。另外,进程通常还包括进程堆栈段(包括临时数据,如方法参数、返回地址和局部变量)和数据段(包含全局变量)。

程序本身不是进程;程序只是被动实体,如存储在磁盘上的文件内容就,而进程是活动实体,它有一个程序计数器用来表示下一个要执行的指令和相关资源集合。

线程

线程(thread),有时称为轻量级进程(lightweight process, LWP),是CPU使用的基本单元;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)。

协程

协程(coroutine),是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。

协程对内核透明,也就是说系统并不知道有协程的存在,完全是由用户程序自己调度的。因为是由用户程序自己控制,那么就很难像抢占式调度那样做到强制的CPU控制权切换到其他进程/线程。通常只能进行协作式调度,需要协程自己主动把控制权转让出去后,其它协程才能被执行到。

goroutine

与Lua、python语言自己实现协程一样,goroutine是golang实现的协程,和传统的协程概念稍有不同,且goroutine并不是直接用线程实现的。

随着编程语言的发展,语言的runtime system开始承担越来越的工作。比如为了提高内存分配效率,会实现一套内存管理机制,如Java、golang等,在语言层实现内存的分配和释放;同理,为了提高多线程情况下的线程切换、分配的效率,golang实现了goroutine,在语言层实现对goroutine的调度和切换工作。

线程和goroutine之间的对比

线程和goroutine之间消耗资源的对比:

  • 内存开销:goroutine消耗更少的内存。通常情况下创建goroutine消耗的内存在2KB左右,线程大概在8MB左右。
  • 调度(切换)开销:线程的调度设计模式切换(用户态到系统态),寄存器等的刷新工作。goroutine这涉及到寄存器的刷新工作。

在此条件下,goroutine的创建和使用非常廉价,我们可以轻松创建上万个goroutine。所以goroutine最大的价值是其实现了并发协程和实际并行执行的线程的映射以及动态扩展。

相信随着其运行库的不断发展和完善,其性能一定会越来越好,尤其是在CPU核数越来越多的未来,终有一天我们会为了代码的简洁和可维护性而放弃那一点点性能的差别。

reference