Onelong

分享知识,与你一起进步......
RSS icon Home icon
  • Linux线程和go协程

    post by onelong / 2016-9-22 21:59 Thursday [工作]
     记得我以前在简书上写过一个篇文章,多线程基础 今晚我想继续聊一聊这个坑,先聊一下最近的惨状吧,中秋前感冒了一次,吃了点感冒了好了,中秋后再来一个半死的感冒,全身疼痛,感觉是被人毒打了一顿,经过几天的悉心治疗,略见好转,想把今天所想的东西写下来。病号突然想去了解一下golang,想了想学习新的东西主要是了解它的特性,就是特有的功能,而不是简单复习一次语言的语法。golang里面最大的不同就是协程,golang里面没有线程这种东西的,可能是谷歌故意放弃了吧,后来细心想了一想,有协程就够了,已经不需要线程了。看了知乎的文章后,http://www.zhihu.com/question/20862617?sort=created 我更加坚信有协程就够了,不需要线程,协程是更小的执行粒子,作为一个java出身,自以为了解操作系统的人,肯定会去对比协程并发和当前多线程并发,于是就引出了一堆问题了。因为操作系统概念里面是没有协程这个东西的,所以语言上面的协程肯定在操作系统上得不到对应的映射。所以第一次猜想协程是依附在操作系统线程上执行的,因为线程是操作系统最小的执行单位。golang在语言级支持了协程,对开发者很友好。协程和线程的对应关系可以是一对一,多对一的,总而言之,尽可能的减少线程上下文(cpu context)的切换,极大提高cpu的利用率。在网络的开发中,我们会遇到很多io阻塞,系统调用阻塞等,这些操作可能会导致中断,切换线程上下文,这些都是不利于我们更有效的使用cpu的。golang很优雅的封装了一套协程api(事件驱动+channel),使得原本需要复杂代码实现东西,变得极其简单高效。事件驱动+channel是golang的灵魂,如果没有这两样东西,golang就什么都不是了,因为用户控制协程切换的途径就是channel等待。golang的协程并不能完全避免线程上下文的切换,在sys call的时候,还是要切换线程的,和操作系统的做法区别不大,到这里我对协程就聊到这里算了,想了解各种细节的人可以看看知乎的文章,尤其是那些图片,这是很有帮助的。下面我来说说另外的坑。

    工作5-6年了,时间算长了,看到一篇文章说golang的协程不是基于用户线程实现的,是库实现的,我就在想难道没用pthread?然后引发一堆狗血。其实我们会接触一堆概念:内核线程,内核进程,轻量级进程,普通进程(用户进程),用户线程等。 确实我很多年前就看过一些相关的文章,那时候看过之后自以为了解了,而今天发现确实想得太少了。对内核而已,是没有进程的,所以没有内核进程,只有内核线程。在Linux的书籍上都是这样说的,“linux线程是
    轻量级进程”,它是一个特殊的进程,没有共享内存而已。内核线程和轻量级进程是一对一的,也就是说对Linux而言,管理线程和管理进程是一样的。下面继续引用一段描述帮我们理清关系吧:
    我们知道,Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和 fork (),最终都用不同的参数调用do_fork()核内API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的,因 此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文 件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。当使用fork系统 调用时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所 有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的"进程"拥有共享的运行环境,只有栈是独立的,由 __clone()传入。 
    Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外 pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为 线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。” 
    从上面可以知道, 
    轻量级进程是用户态调用sys call创建的,而子线程是用master线程创建的,都是需要系统调用的,开销不小。线程池技术是为了减少频繁的创建销毁线程,提高cpu利用率而生的。所以golang协程执行在一个线程池上,至于这个线程池是不是基于pthread lib这个就不好说了,或者Google的牛人直接sys call 实现了,不使用用户线程,不管怎样,协程是新概念,但不是新技术,是另一种比较科学并发模型。最后留下一点点东西吧,https://github.com/cloudwu/coroutine c语言版本的协程,因为没有在语言级别上支持,比较难用,需要消息队列来配合使用,但是原理是相似的。 

    引用地址:
     

    我要评论