赞
踩
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:点击跳转到网站,对人工智能感兴趣的小伙伴可以点进去看看。
本章是Go并发编程的起始篇章,在未来几篇文章中我们会围绕Go并发编程进行理论和实战的学习,欢迎关注我哦!
本章主要以介绍GMP模型为主,偏向于面试和八股,目的是让小伙伴们注重于知识本身,面向面试,面向八股,面向加薪。
Go语言自诞生以来,就以其简洁、高效的并发模型著称。而这其中的核心正是GMP模型。理解GMP模型的演进历程,能帮助我们更好地掌握Go的并发编程。而Goroutine作为Go中的核心概念,极大地简化了并发编程的复杂度。本文将详细阐述Go语言GMP模型的演变过程,并深入解析其设计理念和优点,并详细介绍Goroutine的基本概念、优势及其使用方法,并结合具体代码示例进行说明。
在阅读本文前,先带着以下几个关于GMP模型的面试题目进行思考,以加深理解和掌握:
带着这些问题阅读本文,可以帮助你更系统地掌握GMP模型的核心概念和调度机制,提高面试中的应答能力。
在单进程时代,一个进程就是一个运行中的程序。计算机系统在执行程序时,会从头到尾依次执行完一个程序,然后再执行下一个程序。在这种模型中,不需要复杂的调度机制,因为只有一个执行流程。
为了解决单进程时代的效率问题,引入了多进程和多线程并发模型。在这种模型中,当一个进程阻塞时,CPU可以切换到另一个准备好的进程继续执行。这样可以充分利用CPU资源,提高系统的并发处理能力。
为了解决多进程和多线程带来的高开销和高内存占用问题,引入了协程(Coroutine)。协程是一种比线程更轻量级的执行单元。协程在用户态进行调度,避免了频繁的上下文切换带来的开销。Go语言的GMP模型正是基于协程的设计。
在深入了解Goroutine之前,先来了解一下协程(Coroutine)的基本概念。
用户态线程:
内核态线程:
CPU处理:
Goroutine是Go语言中的协程,实现了轻量级并发。与传统的线程相比,Goroutine具有以下显著特点:
Goroutine非常轻量,初始化时仅占用几KB的栈内存,并且栈内存可以根据需要动态伸缩。这使得我们可以在Go程序中创建成千上万个Goroutine,而不会消耗过多的系统资源。
Goroutine的调度由Go语言的运行时(runtime)负责,而不是操作系统。Go运行时在用户态进行调度,避免了频繁的上下文切换带来的开销,使得调度更加高效。
下面是一个简单的示例,展示了如何在Go语言中使用Goroutine进行并发编程。
package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("Hello") go say("World") time.Sleep(1 * time.Second) fmt.Println("Done") }
在这个示例中,两个Goroutine同时执行,分别打印"Hello"和"World"。通过使用go
关键字,我们可以轻松地启动一个新的Goroutine。
下面的示例展示了如何使用通道来同步多个Goroutine的执行。
package main import ( "fmt" "sync" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d starting\n", id) // 模拟工作 fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) } wg.Wait() fmt.Println("All workers done") }
在这段代码中,使用sync.WaitGroup
来同步多个Goroutine。主Goroutine启动多个子Goroutine并等待它们完成,每个子Goroutine在完成任务后调用wg.Done()
减少计数,主Goroutine调用wg.Wait()
阻塞等待所有子Goroutine完成。
这张图展示了多个Goroutine同时执行的流程以及如何通过通道(Channel)进行同步。
关于waitgroup我会在下一章节中进行详细讲解,欢迎订阅我的频道!在本实例代码中大家了解使用即可。
在Go中,线程是运行Goroutine的实体,而调度器的功能是将可运行的Goroutine分配到工作线程上。Go语言采用了一种高效的Goroutine调度机制,使得程序能够在多核处理器上高效运行。
早期的调度器采用了简单的设计,存在多个缺陷:
概念:用大写的G表示协程,用大写的M表示线程。
问题:
为了克服上述问题,Go引入了GMP模型:
基本概念:
Go语言使用GMP模型来管理并发执行。GMP模型由三个核心组件组成:G(Goroutine)、M(Machine)、P(Processor)。
Goroutine是Go语言中的协程,代表一个独立的执行单元。Goroutine比线程更加轻量级,启动一个Goroutine的开销非常小。Goroutine的调度由Go运行时在用户态进行。
M代表操作系统的线程。M负责实际执行Go代码。一个M可以执行多个Goroutine,但同一时间只能执行一个Goroutine。M与操作系统的线程直接对应,Go运行时通过M来利用多核CPU的并行计算能力。
P代表执行上下文(Processor)。P管理着可运行的Goroutine队列,并负责与M进行绑定。P的数量决定了可以并行执行的Goroutine的数量。Go运行时会根据系统的CPU核数设置P的数量。
GMP模型的组成:
调度器与OS调度器结合:Go的Goroutine调度器与操作系统调度器结合,OS调度器负责将线程分配给CPU执行。
创建两步:
go func()
创建一个协程。在面试中,如果被问到GMP调度模型,建议全面地回答以下内容。如果能完整且详细地讲述这些内容,将会展示你对GMP调度模型的深刻理解和熟练掌握,这将是面试中的亮点。
go func()
创建一个协程。新创建的协程优先保存在P的本地G队列,如果本地队列满了,会将P本地队列中的一半G移入全局队列。我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:csdn面试群。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。