当前位置: 技术问答>linux和unix
[百度分享]用户态线程库 (一)
来源: 互联网 发布时间:2016-11-09
本文导语: 前言 我们编写的绝大多数程序都需要涉及到多线程的并发编程,一般情况下都是采用glibc提供的线程库进行开发。 这里从线程库的实现上来考虑对于并发情况下的编程模式。 从线程说起 线程是什么? 我们的...
前言
我们编写的绝大多数程序都需要涉及到多线程的并发编程,一般情况下都是采用glibc提供的线程库进行开发。
这里从线程库的实现上来考虑对于并发情况下的编程模式。
从线程说起
线程是什么?
我们的多数程序都是运行在多线程模式下,通过同时并发处理多个任务来处理不同的请求。线程的特点是共享地址空间,从而高效的共享数据。与进程相比最大特点在于对于共享空间的简单处理,虽然进程间的数据共享采用mmap方式也很方便,但是毕竟涉及到共享内存的使用,需要有一定的注意,和使用要求。 多线程的概念引入,可以使得我们在一个进程中可以同时处理多个业务逻辑,而不受影响(共享数据有同步原语支持)。在多核环境下也可以有效的利用多CPU的优势。
本质上来说, 线程在实现上采用clone的调用方式利用在堆空间上分配的内存作为,每个线程的栈空间(我们的环境中是10M或者8M,采用ulimt -s 可以看到)都是独立,包含了当前CPU的信息。目前常用的是2.6内核(部分32位机器运行在2.4内核下),2.4内核中是采用进程来模拟线程,用ps命令可以看到有许多个进程,在ps命令看来,线程基本上是等同于进程。2.6内核中一个多线程的进程,只会显示一个进程,但用gdb 或者 pstack 查看程序依然可以看到
pthread_t 的意义, pthread_create创建线程的时候我们会拿到一个pthread_t 的返回值,在ullog中拿他做为线程号的标记,这个值本质上是维护线程数据的指针地址。 在ullog中采用这个作为线程的标记其实是存在问题的,假设这样的情况: 一个现程在启动后运行一段时间才退出,而后再启动,这个时候这个线程的数据依然会被复用,这个时候就出现了前后这两个在我们概念中是不一样的两个线程被认为是线程号相同的情况. 虽然我们在线程启动和结束的时候都会有标记特殊的日志
我们的glibc pthread的实现中,默认栈空间是10M(一些机器上是8M),这些可以在线程启动的时候进行修改。栈空间本质上也是通过mmap的方式从堆上分配出来提供给没个线程独立使用。在具体的实现上,栈与栈之间一般都会存在一部分空间是不可写同时也不可读的,这也是为什么我们一旦栈溢出就很容易出core的原因。 栈空间的大小可以在线程启动的时候修改或者通过ulimit方式进行强制的改变。
注:如果不考虑进程的可写数据(只读都是一样)的共享,多进程方式对比多线程其实优势更大,而且还有良好的安全性。
线程调度
在线程库中对于线程的分配一般是受到内存大小的控制, 而CPU的数量往往不多, 这里CPU在运行的时候就涉及到,如何去运作这些线程。每个线程只有获得CPU的使用权才能执行指令.所谓的多线程的并发运行,从 宏观上看.其实就是各个线程轮流或者CPU的使用权,分别执行各自的任务.在可运行池中,会有多个处于就绪状态的线程在等待CPU。
在我们使用的线程库中,调度往往需要考虑下面的方式
时间片,每个线程CPU运行一段时间后,主动让出CPU
阻塞或休眠, 比如sleep, 等待锁,系统调用等操作本身会让CPU出现一定程度上的上下文切换
线程结束
主动放弃CPU, 在我们的环境中是sched_yield, 可以主动让出CPU。
在我们现在64位机器上使用的都是基于nptl线程库,相比32位中2.4内核下的linuxthread本身有很的大的提高。基本的切换上相差了近1倍。
虽然操作系统中已经考虑了不少调度的问题,但是这些是实际中的表现往往不尽人意
时间片不可控,一些CPU消耗性程序其实可以多运行一部分时间
上下文切换有一定的内核态用户态的切换。
大量的锁应用, 对性能会带来一定的影响
当存在大量长时间堵塞的时候, 需要大量的线程支持, 会对系统产生负面影响
这样的基础上,我们考虑采用一些其他方式来解决这些问题, 比如异步编程。
我们编写的绝大多数程序都需要涉及到多线程的并发编程,一般情况下都是采用glibc提供的线程库进行开发。
这里从线程库的实现上来考虑对于并发情况下的编程模式。
从线程说起
线程是什么?
我们的多数程序都是运行在多线程模式下,通过同时并发处理多个任务来处理不同的请求。线程的特点是共享地址空间,从而高效的共享数据。与进程相比最大特点在于对于共享空间的简单处理,虽然进程间的数据共享采用mmap方式也很方便,但是毕竟涉及到共享内存的使用,需要有一定的注意,和使用要求。 多线程的概念引入,可以使得我们在一个进程中可以同时处理多个业务逻辑,而不受影响(共享数据有同步原语支持)。在多核环境下也可以有效的利用多CPU的优势。
本质上来说, 线程在实现上采用clone的调用方式利用在堆空间上分配的内存作为,每个线程的栈空间(我们的环境中是10M或者8M,采用ulimt -s 可以看到)都是独立,包含了当前CPU的信息。目前常用的是2.6内核(部分32位机器运行在2.4内核下),2.4内核中是采用进程来模拟线程,用ps命令可以看到有许多个进程,在ps命令看来,线程基本上是等同于进程。2.6内核中一个多线程的进程,只会显示一个进程,但用gdb 或者 pstack 查看程序依然可以看到
pthread_t 的意义, pthread_create创建线程的时候我们会拿到一个pthread_t 的返回值,在ullog中拿他做为线程号的标记,这个值本质上是维护线程数据的指针地址。 在ullog中采用这个作为线程的标记其实是存在问题的,假设这样的情况: 一个现程在启动后运行一段时间才退出,而后再启动,这个时候这个线程的数据依然会被复用,这个时候就出现了前后这两个在我们概念中是不一样的两个线程被认为是线程号相同的情况. 虽然我们在线程启动和结束的时候都会有标记特殊的日志
我们的glibc pthread的实现中,默认栈空间是10M(一些机器上是8M),这些可以在线程启动的时候进行修改。栈空间本质上也是通过mmap的方式从堆上分配出来提供给没个线程独立使用。在具体的实现上,栈与栈之间一般都会存在一部分空间是不可写同时也不可读的,这也是为什么我们一旦栈溢出就很容易出core的原因。 栈空间的大小可以在线程启动的时候修改或者通过ulimit方式进行强制的改变。
注:如果不考虑进程的可写数据(只读都是一样)的共享,多进程方式对比多线程其实优势更大,而且还有良好的安全性。
线程调度
在线程库中对于线程的分配一般是受到内存大小的控制, 而CPU的数量往往不多, 这里CPU在运行的时候就涉及到,如何去运作这些线程。每个线程只有获得CPU的使用权才能执行指令.所谓的多线程的并发运行,从 宏观上看.其实就是各个线程轮流或者CPU的使用权,分别执行各自的任务.在可运行池中,会有多个处于就绪状态的线程在等待CPU。
在我们使用的线程库中,调度往往需要考虑下面的方式
时间片,每个线程CPU运行一段时间后,主动让出CPU
阻塞或休眠, 比如sleep, 等待锁,系统调用等操作本身会让CPU出现一定程度上的上下文切换
线程结束
主动放弃CPU, 在我们的环境中是sched_yield, 可以主动让出CPU。
在我们现在64位机器上使用的都是基于nptl线程库,相比32位中2.4内核下的linuxthread本身有很的大的提高。基本的切换上相差了近1倍。
虽然操作系统中已经考虑了不少调度的问题,但是这些是实际中的表现往往不尽人意
时间片不可控,一些CPU消耗性程序其实可以多运行一部分时间
上下文切换有一定的内核态用户态的切换。
大量的锁应用, 对性能会带来一定的影响
当存在大量长时间堵塞的时候, 需要大量的线程支持, 会对系统产生负面影响
这样的基础上,我们考虑采用一些其他方式来解决这些问题, 比如异步编程。
|
辛苦~~写了这么多~~
|
在DELPHI中有用過線程,簡單點可以理解同時處理多個任務,讓程式運行速度變得更快
|
求全文!谢啦!
|
哦求全文论述
|
我觉得多线程对比多进程的另外一个优势是开销小
|
很好很有用,永远支持西电!
|
期待更新~!
|
学习 一下 不错
|
有全文链接么发下谢谢
|
倍。
虽然操作系统中已经考虑了不少调度的问题,但是这些是实际中的表现往往不尽人意
时间片不可控,一些CPU消耗性程序其实可以多运行一部分时间
上下文切换有一定的内核态用户态的切换。
虽然操作系统中已经考虑了不少调度的问题,但是这些是实际中的表现往往不尽人意
时间片不可控,一些CPU消耗性程序其实可以多运行一部分时间
上下文切换有一定的内核态用户态的切换。
|
说的很不错,学习了 呵呵很好啊
|
看看先
|
谢谢,很好.
|
了解了解。
|
学习学习
|
太辛苦咯。。。
|
顶文章的作者 让我们了解了当今开放软件的知识 希望你经常为我们发表类似的文章
|
学习了,顶作者
|
学习自己不知道的
|
很好很有用,永远支持西电!
|
路过,路过,学习