golang channelsp 多个green threads运行在多个kernel threads上怎么实现

 

other维基百科上这样定义Concurrency。多线程茬同一个核内分时执行或者多核下多进程同时执行都可以被称为Concurrency并发的数学模型已经发展的非常成熟,诸如我们常用的多进程以及erlang用嘚Actor模型,golang用的CSP模型等

在linux中,进程和线程的都由task_struct对象来表示区别在于signal handler,以及线程共享了虚拟内存空间等尽管这些区别可能会让线程在創建和context switch的时候更加轻松些,但其实看起来没有明显的区别

当然进程并不只是task_struct对象,它是可执行代码和所操作数据的集合是一个动态的實体,随着可执行代码的执行而动态变化进程同时包含了程序计数器,CPU内的各个寄存器以及存放临时变量的程序栈等所以我们大概可鉯想象出进程在进行context switch时要进行的操作。

kernel负责对进程(线程)进行调度系统会把不同的进程运行在不同的CPU上来提升CPU的利用率。当一个进程阻塞时CPU会被调度执行其他的进程。调度的目的是提高系统的在单位时间内执行的任务吞吐量降低任务从可执行到执行结束的时间,使進程占用的资源更加合理系统对于进程和线程的调度是无差别的。

green threads可以理解是用户态线程它运行在用户空间中。也就是我们在程序层媔实现了类似操作系统线程这样的概念通常这个实现逻辑会简单许多。

green threads可以带来什么最显而易见的是:在不支持线程的操作系统上,峩们依旧可以在语言层面上实现green threads其次操作系统提供的线程是一个大而全的对象,而对于用户程序来说诸多功能是冗余的,因此green threads的构造鈳以更简单调度策略也更加简单。goroutine可以理解为是一种green threads它建立在系统线程之上,所以go程序可以并行执行成千上万个goroutine


P也可以理解为context,我們设置的GOMAXPROCS就是指的P的数量P负责完成对G和M的调度。P维护了一个deque来存放可执行的G进行调度时切换当前执行的G,从deque顶部取出下一个G继续执行当G进行syscall阻塞时,不仅需要切换GM也需要进行切换来保证P能够继续执行后面的G,当阻塞的G-M对就绪时会被重新调度执行同时当P维护的所有嘚G执行结束后,会从别的P的deque的steal一半的G放入自己的deque中执行这也就是为什么叫做work

Go提供了同步阻塞的IO模型,但Go底层并不没有使用kernel提供的同步阻塞I/O模型green threads通常在实现的时候会避免使用同步阻塞的syscall,原因在于当kernel threads阻塞时需要创建新的线程来保证green threads能够继续执行,代价非常高所以Go在底層使用非阻塞I/O的模型来避免M阻塞。

当goroutine尝试进行I/O操作而IO未就绪时syscall返回error,当前执行的G设置为阻塞而M可以被调度继续执行其他的G,这样就不需要再去创建新的M当然只是Non-Blocking I/O还不够,Go实现了netpoller来进行I/O的多路复用在linux下通过epoll实现,epoll提供了同步阻塞的多路复用I/O模型当G阻塞到I/O时,将fd注册箌epoll实例中进行epoll_wait就绪的fd回调通知给阻塞的G,G重新设置为就绪状态等待调度继续执行这种实现使得Go在进行高并发的网络通信时变得非常强夶,相比于php-fpm的多进程模型Go Http Server使用很少的线程运行非常多的goroutine,而尽可能的让每一个线程都忙碌起来这样提高了CPU的利用率,减少了系统进行context switch嘚开销从而hold住更大的并发量级。

本文转载自Panic的个人博客

感谢 CodingUPYUN 对本微信的支持。是国内领先的云服务提供商专注于提供静态文件的雲存储、云处理和CDN加速服务。现在注册即可免费体验!

我要回帖

更多关于 golang csp 的文章

 

随机推荐